2
0

Compare commits

...

17 Commits

Author SHA1 Message Date
Maarten Billemont
2886e040a1 Warning fix. 2020-09-03 11:04:59 -04:00
Maarten Billemont
01cea659ca Bump Pearl for nullability fixes. 2020-09-03 10:52:56 -04:00
Maarten Billemont
3a18e02a87 Revert "Xcode 12 update of xcdatamodel."
This reverts commit 2de57984b2.

NSSecureUnarchiveFromDataTransformer is not compatible with iOS 9-11
2020-09-03 10:41:05 -04:00
Maarten Billemont
2de57984b2 Xcode 12 update of xcdatamodel. 2020-09-03 10:39:23 -04:00
Maarten Billemont
c7201c7d90 Update for Xcode 12 & build fixes. 2020-09-03 09:53:08 -04:00
Maarten Billemont
d62c6b4594 Sites no longer load with batch requests & load improvements. 2020-09-03 09:52:08 -04:00
Maarten Billemont
57f275c471 Update for Xcode 12 & add device identifier to UI. 2020-09-02 16:40:41 -04:00
Maarten Billemont
b1d8296396 Add nonstandard output type tests for i,r + fix indentation. 2020-08-29 09:48:14 -04:00
Maarten Billemont
6d25463de0 Rename next-gen to Spectre. 2020-07-21 21:21:52 -04:00
Maarten Billemont
029041dcf7 Expand the maximum length of query searches. 2020-07-11 21:43:34 -04:00
Maarten Billemont
cfbf1f5cac Use visibility instead of gone so constraints are managed by stack view. 2020-07-11 10:48:19 -04:00
Maarten Billemont
acbd2dc2cc Include purchased features in export file. 2020-07-06 22:28:15 -04:00
Maarten Billemont
8fcac65fd5 Additional documentation for parameter contracts. 2020-07-06 22:27:47 -04:00
Maarten Billemont
9904f4c715 Try to detect if cipherText is plainText.
In some situations, the cipherText that was passed in is actually
plainText.  Old mpsites files used to store the login name as plain text
even though the file was redacted.  Newer versions of the file store the
login name as ciphertext.  There is no clear way to distinguish between
the two cases.
2020-07-06 14:18:47 -04:00
Maarten Billemont
b51a3de32c Check pasteboard when app enters foreground, not activation. 2020-07-05 20:24:59 -04:00
Maarten Billemont
9e91f0a9d6 More reliable monitoring of changes using NSFetchedResultsController. 2020-07-05 20:24:18 -04:00
Maarten Billemont
7368b1be90 Source is button item, not a view. 2020-05-24 10:54:25 -04:00
33 changed files with 669 additions and 586 deletions

View File

@@ -332,7 +332,6 @@
DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BEB1711E2DC00CF925C /* MPPreferencesViewController.m */; };
DABD3C1F1711E2DC00CF925C /* MPTypeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BED1711E2DC00CF925C /* MPTypeViewController.m */; };
DABD3C211711E2DC00CF925C /* MPiOSConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BF11711E2DC00CF925C /* MPiOSConfig.m */; };
DABD3C241711E2DC00CF925C /* MasterPassword.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = DABD3BF81711E2DC00CF925C /* MasterPassword.entitlements */; };
DABD3C251711E2DC00CF925C /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DABD3BF91711E2DC00CF925C /* Settings.bundle */; };
DABD3C261711E2DC00CF925C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DABD3BFA1711E2DC00CF925C /* InfoPlist.strings */; };
DABD3C271711E2DC00CF925C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BFC1711E2DC00CF925C /* main.m */; };
@@ -3446,7 +3445,6 @@
DA5E0E5E24589C9B0007FBA7 /* Icon-83@2x.png in Resources */,
DA854C8418D4CFBF00106317 /* avatar-add.png in Resources */,
DAA1764B19D8B82B0044227B /* login_name@2x.png in Resources */,
DABD3C241711E2DC00CF925C /* MasterPassword.entitlements in Resources */,
DABD3C251711E2DC00CF925C /* Settings.bundle in Resources */,
DABD3C261711E2DC00CF925C /* InfoPlist.strings in Resources */,
DA32D05119D3D107004F3F0E /* icon_meter.png in Resources */,

View File

@@ -101,7 +101,6 @@
DA5E5D011724A667003798D8 /* MPKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CAE1724A667003798D8 /* MPKey.m */; };
DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */; };
DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB61724A667003798D8 /* MPMacConfig.m */; };
DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CBF1724A667003798D8 /* MasterPassword.entitlements */; };
DA5E5D0A1724A667003798D8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CC21724A667003798D8 /* InfoPlist.strings */; };
DA5E5D0B1724A667003798D8 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CC41724A667003798D8 /* MainMenu.xib */; };
DA5E5D0C1724A667003798D8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CC61724A667003798D8 /* main.m */; };
@@ -2388,7 +2387,7 @@
CLASSPREFIX = MP;
LastSwiftUpdateCheck = 0720;
LastTestingUpgradeCheck = 0510;
LastUpgradeCheck = 1140;
LastUpgradeCheck = 1200;
ORGANIZATIONNAME = Lyndir;
TargetAttributes = {
DA1C7AA61F1A8F24009A3551 = {
@@ -2535,7 +2534,6 @@
DACA29671705DF81002C6C22 /* SourceCodePro-ExtraLight.otf in Resources */,
DACA29681705DF81002C6C22 /* SourceCodePro-Black.otf in Resources */,
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */,
DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */,
DA5E5D0A1724A667003798D8 /* InfoPlist.strings in Resources */,
DA5E5D0B1724A667003798D8 /* MainMenu.xib in Resources */,
DA0933CC1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png in Resources */,
@@ -3026,6 +3024,7 @@
CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
@@ -3127,6 +3126,7 @@
CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
@@ -3202,6 +3202,7 @@
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = Source/Mac/MasterPassword.entitlements;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
ENABLE_HARDENED_RUNTIME = YES;
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
@@ -3241,6 +3242,7 @@
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = Source/Mac/MasterPassword.entitlements;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
ENABLE_HARDENED_RUNTIME = YES;
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1140"
version = "1.3">
LastUpgradeVersion = "1200"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
@@ -27,15 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "Master Password.app"
BlueprintName = "MasterPassword-macOS"
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1140"
version = "1.3">
LastUpgradeVersion = "1200"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
@@ -27,15 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA1C7AC61F1A8FD8009A3551"
BuildableName = "mpw-bench"
BlueprintName = "mpw-bench"
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1140"
LastUpgradeVersion = "1200"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
@@ -27,15 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA1C7AA61F1A8F24009A3551"
BuildableName = "mpw-cli"
BlueprintName = "mpw-cli"
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>
@@ -81,10 +72,6 @@
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
<LocationScenarioReference
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1">
</LocationScenarioReference>
<EnvironmentVariables>
<EnvironmentVariable
key = "TERM"
@@ -92,6 +79,10 @@
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<LocationScenarioReference
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1">
</LocationScenarioReference>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1140"
version = "1.3">
LastUpgradeVersion = "1200"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
@@ -27,15 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA67743A1A474A03004F356A"
BuildableName = "mpw-test"
BlueprintName = "mpw-test"
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>

View File

@@ -19,6 +19,8 @@
#import "MPAppDelegate_Store.h"
#import "mpw-marshal.h"
#import "mpw-util.h"
#import "MPAppDelegate_InApp.h"
#import "MPSecrets.h"
#if TARGET_OS_IPHONE
#define STORE_OPTIONS NSPersistentStoreFileProtectionKey : NSFileProtectionComplete,
@@ -181,12 +183,12 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
} );
// Do nothing if already fully set up, otherwise (re-)load the store.
if (self.storeCoordinator && self.mainManagedObjectContext && self.privateManagedObjectContext)
if (self.mainManagedObjectContext && self.privateManagedObjectContext)
return;
[self.storeQueue addOperationWithBlock:^{
// Do nothing if already fully set up, otherwise (re-)load the store.
if (self.storeCoordinator && self.mainManagedObjectContext && self.privateManagedObjectContext)
if (self.mainManagedObjectContext && self.privateManagedObjectContext)
return;
// Unregister any existing observers and contexts.
@@ -199,6 +201,12 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
[self.privateManagedObjectContext reset];
self.privateManagedObjectContext = nil;
}];
NSError *error = nil;
for (NSPersistentStore *store in self.storeCoordinator.persistentStores)
if (![self.storeCoordinator removePersistentStore:store error:&error] || error) {
MPError( error, @"Couldn't remove persistence store from coordinator." );
return;
}
// Don't load when the store is corrupted.
if ([self.storeCorrupted boolValue])
@@ -207,33 +215,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
// Check if migration is necessary.
[self migrateStore];
// Install managed object contexts and observers.
self.privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[self.privateManagedObjectContext performBlockAndWait:^{
self.privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
self.privateManagedObjectContext.persistentStoreCoordinator = self.storeCoordinator;
}];
self.mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
self.mainManagedObjectContext.parentContext = self.privateManagedObjectContext;
if (@available( iOS 10.0, macOS 10.12, * ))
self.mainManagedObjectContext.automaticallyMergesChangesFromParent = YES;
else
// When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext.
PearlAddNotificationObserverTo( self.mainManagedObjectContext, NSManagedObjectContextDidSaveNotification,
self.privateManagedObjectContext, nil, ^(NSManagedObjectContext *mainContext, NSNotification *note) {
[mainContext performBlock:^{
@try {
[mainContext mergeChangesFromContextDidSaveNotification:note];
}
@catch (NSException *exception) {
err( @"While merging changes:\n%@", [exception fullDescription] );
}
}];
} );
// Create a new store coordinator.
NSError *error = nil;
NSURL *localStoreURL = [self localStoreURL];
if (![[NSFileManager defaultManager] createDirectoryAtURL:[localStoreURL URLByDeletingLastPathComponent]
withIntermediateDirectories:YES attributes:nil error:&error]) {
@@ -259,6 +241,29 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
}
self.storeCorrupted = @NO;
// Install managed object contexts and observers.
self.privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
self.privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
self.privateManagedObjectContext.persistentStoreCoordinator = self.storeCoordinator;
self.mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
self.mainManagedObjectContext.parentContext = self.privateManagedObjectContext;
if (@available( iOS 10.0, macOS 10.12, * ))
self.mainManagedObjectContext.automaticallyMergesChangesFromParent = YES;
else
// When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext.
PearlAddNotificationObserverTo( self.mainManagedObjectContext, NSManagedObjectContextDidSaveNotification,
self.privateManagedObjectContext, nil, ^(NSManagedObjectContext *mainContext, NSNotification *note) {
[mainContext performBlock:^{
@try {
[mainContext mergeChangesFromContextDidSaveNotification:note];
}
@catch (NSException *exception) {
err( @"While merging changes:\n%@", [exception fullDescription] );
}
}];
} );
#if TARGET_OS_IPHONE
PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, UIApp, [NSOperationQueue mainQueue],
^(MPAppDelegate_Shared *self, NSNotification *note) {
@@ -721,7 +726,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
error:(__autoreleasing NSError **)error {
MPMarshalledUser *exportUser = NULL;
MPMarshalledFile *exportFile = NULL;
MPMarshalledFile *exportFile = mpw_marshal_file( NULL, NULL, mpw_marshal_data_new() );
@try {
inf( @"Exporting sites, %@, for user: %@", revealPasswords? @"revealing passwords": @"omitting passwords", user.userID );
NSString *masterPassword = askExportPassword( user.name );
@@ -730,6 +735,11 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
return nil;
}
for (NSString *feature in @[MPProductGenerateLogins, MPProductGenerateAnswers, MPProductOSIntegration, MPProductTouchID])
if ([[MPAppDelegate_Shared get] isFeatureUnlocked:feature])
mpw_marshal_data_set_str( digest( strf( @"%@/%@", user.name, feature )).UTF8String, exportFile->data,
"user", "_ext_mpw", feature.UTF8String, nil );
MPKey *key = [[MPKey alloc] initForFullName:user.name withMasterPassword:masterPassword];
exportUser = mpw_marshal_user( user.name.UTF8String,
mpw_masterKeyProvider_str( masterPassword.UTF8String ), user.algorithm.version );

View File

@@ -29,6 +29,8 @@
if ([self hasChanges])
[self performBlockAndWait:^{
@try {
[self processPendingChanges];
NSError *error = nil;
if (!(success = [self save:&error]))
MPError( error, @"While saving." );

View File

@@ -16,7 +16,9 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
MP_LIBS_BEGIN
#import <Sentry/Sentry.h>
MP_LIBS_END
__BEGIN_DECLS
extern NSString *const MPErrorDomain;

View File

@@ -22,10 +22,12 @@
#import "MPSecrets.h"
#import "mpw-marshal.h"
MP_LIBS_BEGIN
#import <Carbon/Carbon.h>
#import <ServiceManagement/ServiceManagement.h>
#import <Sentry/Sentry.h>
#import <Countly/Countly.h>
MP_LIBS_END
#define LOGIN_HELPER_BUNDLE_ID @"com.lyndir.lhunath.MasterPassword.Mac.LoginHelper"
@@ -71,7 +73,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[SentrySDK startWithOptions:@{
@"dsn" : NilToNSNull( decrypt( sentryDSN ) ),
#ifdef DEBUG
@"debug" : @(YES),
@"debug" : @(NO),
@"environment" : @"Development",
#elif PUBLIC
@"debug" : @(NO),

View File

@@ -617,7 +617,7 @@
NSMutableString *queryPattern = [NSMutableString new];
[queryString enumerateSubstringsInRange: NSMakeRange(0, [queryString length]) options: NSStringEnumerationByComposedCharacterSequences
usingBlock: ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
if (substringRange.location < 10) {
if (substringRange.location < 20) {
[queryGroups addObject:substring];
[queryPattern appendString:@"*"];
}

View File

@@ -102,7 +102,7 @@
attributes = {
BuildIndependentTargetsInParallel = YES;
CLASSPREFIX = MP;
LastUpgradeCheck = 1140;
LastUpgradeCheck = 1200;
ORGANIZATIONNAME = "Maarten Billemont";
TargetAttributes = {
DAD9B5C0176299B9001835F9 = {
@@ -185,6 +185,7 @@
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
@@ -271,6 +272,7 @@
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;

View File

@@ -58,22 +58,6 @@ const long MPAvatarAdd = 10000;
self.avatarImageView.layer.masksToBounds = NO;
self.avatarImageView.backgroundColor = [UIColor clearColor];
[self observeKeyPath:@"bounds" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *self) {
self.contentView.frame = self.bounds;
}];
[self observeKeyPath:@"selected" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *self) {
[self updateAnimated:self.superview != nil];
}];
[self observeKeyPath:@"highlighted" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *self) {
[self updateAnimated:self.superview != nil];
}];
PearlAddNotificationObserver( UIKeyboardWillShowNotification, nil, [NSOperationQueue mainQueue],
^(MPAvatarCell *self, NSNotification *note) {
CGRect keyboardRect = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat keyboardHeight = CGRectGetHeight( self.window.screen.bounds ) - CGRectGetMinY( keyboardRect );
[self.keyboardHeightConstraint updateConstant:keyboardHeight];
} );
CABasicAnimation *toShadowOpacityAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
toShadowOpacityAnimation.toValue = @0.2f;
toShadowOpacityAnimation.duration = 0.5f;
@@ -91,6 +75,22 @@ const long MPAvatarAdd = 10000;
self.targetedShadowAnimation.duration = MAXFLOAT;
self.avatarImageView.layer.shadowColor = [UIColor whiteColor].CGColor;
self.avatarImageView.layer.shadowOffset = CGSizeZero;
[self observeKeyPath:@"bounds" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *self) {
self.contentView.frame = self.bounds;
}];
[self observeKeyPath:@"selected" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *self) {
[self updateAnimated:self.superview != nil];
}];
[self observeKeyPath:@"highlighted" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *self) {
[self updateAnimated:self.superview != nil];
}];
PearlAddNotificationObserver( UIKeyboardWillShowNotification, nil, [NSOperationQueue mainQueue],
^(MPAvatarCell *self, NSNotification *note) {
CGRect keyboardRect = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat keyboardHeight = CGRectGetHeight( self.window.screen.bounds ) - CGRectGetMinY( keyboardRect );
[self.keyboardHeightConstraint updateConstant:keyboardHeight];
} );
}
- (void)prepareForReuse {

View File

@@ -30,8 +30,10 @@
@property(weak, nonatomic) IBOutlet UIActivityIndicatorView *activity;
@property(weak, nonatomic) IBOutlet UIButton *passwordButton;
@property(weak, nonatomic) IBOutlet UIView *tipContainer;
@property(weak, nonatomic) IBOutlet UIButton *deviceButton;
- (IBAction)controlChanged:(UIControl *)control;
- (IBAction)copyPassword:(UITapGestureRecognizer *)recognizer;
- (IBAction)copyDevice:(id)sender;
@end

View File

@@ -38,6 +38,8 @@
self.view.backgroundColor = [UIColor clearColor];
self.dialogView.layer.cornerRadius = 5;
[self.deviceButton setTitle:[PearlKeyChain deviceIdentifier] forState:UIControlStateNormal];
}
- (void)viewWillAppear:(BOOL)animated {
@@ -112,6 +114,11 @@
}];
}
- (IBAction)copyDevice:(id)sender {
[UIPasteboard generalPasteboard].string = [PearlKeyChain deviceIdentifier];
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Device Identifier Copied" ) dismissAfter:2];
}
#pragma mark - Private
- (void)updateKey {

View File

@@ -17,7 +17,6 @@
//==============================================================================
#import "MPGuideViewController.h"
#import "markdown_lib.h"
#import "NSString+MPMarkDown.h"
@interface MPGuideStep : NSObject

View File

@@ -435,7 +435,7 @@
}];
}
- (IBAction)doContent:(id)sender {
- (IBAction)doContent:(UIButton *)sender {
[UIView animateWithDuration:.2f animations:^{
self.contentButton.selected = YES;
@@ -537,13 +537,13 @@
// UI
//self.backgroundColor = mainSite.url? [UIColor greenColor]: [UIColor redColor];
self.upgradeButton.gone = !mainSite.requiresExplicitMigration && ![[MPiOSConfig get].allowDowngrade boolValue];
self.answersButton.gone = ![[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateAnswers];
self.upgradeButton.visible = mainSite.requiresExplicitMigration || [[MPiOSConfig get].allowDowngrade boolValue];
self.answersButton.visible = [[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateAnswers];
BOOL settingsMode = self.mode == MPPasswordCellModeSettings;
self.loginNameContainer.visible = settingsMode || mainSite.loginGenerated || [mainSite.loginName length];
self.modeButton.visible = !self.transientSite;
self.modeButton.alpha = settingsMode? 0.5f: 0.1f;
self.counterLabel.visible = self.counterButton.visible = mainSite.type & MPResultTypeClassTemplate;
self.counterLabel.visible = self.counterButton.visible = (mainSite.type & MPResultTypeClassTemplate) == MPResultTypeClassTemplate;
self.modeButton.selected = settingsMode;
self.strengthLabel.gone = !settingsMode;
self.modeScrollView.scrollEnabled = !self.transientSite;

View File

@@ -30,8 +30,8 @@
@property(nonatomic, strong) IBOutlet UIView *badNameTipContainer;
@property(nonatomic, strong) IBOutlet UIView *popdownView;
@property(nonatomic, strong) IBOutlet UIView *popdownContainer;
@property(nonatomic, strong) IBOutlet UIView *voltoInstallAlert;
@property(nonatomic, strong) IBOutlet UIView *voltoMigrateAlert;
@property(nonatomic, strong) IBOutlet UIView *spectreInstallAlert;
@property(nonatomic, strong) IBOutlet UIView *spectreMigrateAlert;
@property(assign, nonatomic) BOOL active;
@@ -39,6 +39,6 @@
- (void)reloadSites;
- (IBAction)dismissPopdown:(id)sender;
- (IBAction)upgradeVolto:(UIButton *)sender;
- (IBAction)upgradeSpectre:(UIButton *)sender;
@end

View File

@@ -16,7 +16,9 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
MP_LIBS_BEGIN
#import <StoreKit/StoreKit.h>
MP_LIBS_END
#import "MPSitesViewController.h"
#import "MPiOSAppDelegate.h"
@@ -72,7 +74,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
[self registerObservers];
[self updateConfigKey:nil];
[self updateVoltoAlerts];
[self updateSpectreAlerts];
static NSRegularExpression *bareHostRE = nil;
static dispatch_once_t once = 0;
@@ -199,11 +201,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
if (controller == self.fetchedResultsController)
PearlMainQueue( ^{
[self.collectionView updateDataSource:self.dataSource
toSections:[self createDataSource]
reloadItems:nil completion:nil];
} );
[self updateSites];
}
#pragma mark - UISearchBarDelegate
@@ -320,7 +318,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue],
^(MPSitesViewController *self, NSNotification *note) {
[self viewWillAppear:YES];
[self updateVoltoAlerts];
[self updateSpectreAlerts];
} );
PearlAddNotificationObserver( MPSignedOutNotification, nil, nil,
^(MPSitesViewController *self, NSNotification *note) {
@@ -373,7 +371,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
NSMutableString *queryPattern = [NSMutableString new];
[queryString enumerateSubstringsInRange: NSMakeRange(0, [queryString length]) options: NSStringEnumerationByComposedCharacterSequences
usingBlock: ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
if (substringRange.location < 10) {
if (substringRange.location < 20) {
[queryGroups addObject:substring];
[queryPattern appendString:@"*"];
}
@@ -385,20 +383,25 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
NSError *error = nil;
self.fetchedResultsController.fetchRequest.predicate =
[NSPredicate predicateWithFormat:@"name LIKE[cd] %@ AND user == %@", queryPattern, [MPiOSAppDelegate get].activeUserOID];
if (![self.fetchedResultsController performFetch:&error])
if (![self.fetchedResultsController performFetch:&error] || error)
MPError( error, @"Couldn't fetch sites." );
PearlMainQueue( ^{
[self.collectionView updateDataSource:self.dataSource
toSections:[self createDataSource]
reloadItems:@[ MPTransientPasswordItem ] completion:^(BOOL finished) {
for (MPSiteCell *cell in self.collectionView.visibleCells)
[cell setQueryGroups:self.queryGroups];
}];
} );
[self updateSites];
}];
}
- (void)updateSites {
PearlMainQueue( ^{
[self.collectionView updateDataSource:self.dataSource
toSections:[self createDataSource]
reloadItems:@[ MPTransientPasswordItem ] completion:^(BOOL finished) {
for (MPSiteCell *cell in self.collectionView.visibleCells)
[cell setQueryGroups:self.queryGroups];
}];
} );
}
#pragma mark - Properties
- (NSString *)query {
@@ -416,14 +419,19 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
if (!_fetchedResultsController) {
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
NSFetchRequest *fetchRequest = [MPSiteEntity fetchRequest];
fetchRequest.sortDescriptors = @[
[[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
];
fetchRequest.fetchBatchSize = 10;
(self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:mainContext
sectionNameKeyPath:nil cacheName:nil]).delegate = self;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error] || error)
MPError( error, @"Couldn't fetch sites." );
[self updateSites];
}];
[self registerObservers];
}
@@ -457,23 +465,23 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
self.popdownToTopConstraint.priority = UILayoutPriorityDefaultHigh;
}
- (IBAction)upgradeVolto:(UIButton *)sender {
- (IBAction)upgradeSpectre:(UIButton *)sender {
[[MPiOSAppDelegate get] migrateFor:[MPiOSAppDelegate get].activeUserForMainThread];
}
#pragma mark - Private
- (void)updateVoltoAlerts {
- (void)updateSpectreAlerts {
BOOL voltoInstalled = [UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto:"]];
if (voltoInstalled) {
self.voltoInstallAlert.visible = NO;
self.voltoMigrateAlert.visible = YES;
BOOL spectreInstalled = [UIApp canOpenURL:[[NSURL alloc] initWithString:@"spectre:"]];
if (spectreInstalled) {
self.spectreInstallAlert.visible = NO;
self.spectreMigrateAlert.visible = YES;
}
else {
self.voltoInstallAlert.visible = [MPiOSAppDelegate get].voltoViewController != nil;
self.voltoMigrateAlert.visible = NO;
self.spectreInstallAlert.visible = [MPiOSAppDelegate get].spectreViewController != nil;
self.spectreMigrateAlert.visible = NO;
}
}

View File

@@ -18,10 +18,13 @@
#import "MPStoreViewController.h"
#import "MPiOSAppDelegate.h"
#import "UIColor+Expanded.h"
#import "MPAppDelegate_InApp.h"
#import "MPSitesViewController.h"
MP_LIBS_BEGIN
#import "UIColor+Expanded.h"
MP_LIBS_END
PearlEnum( MPDevelopmentFuelConsumption,
MPDevelopmentFuelConsumptionQuarterly, MPDevelopmentFuelConsumptionMonthly, MPDevelopmentFuelWeekly );

View File

@@ -48,7 +48,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
MPActiveUserStateMinimized,
};
@interface MPUsersViewController()
@interface MPUsersViewController()<NSFetchedResultsControllerDelegate>
@property(nonatomic) MPActiveUserState activeUserState;
@property(nonatomic, strong) NSArray *userIDs;
@@ -57,7 +57,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
@property(nonatomic) NSUInteger marqueeTipTextIndex;
@property(nonatomic, copy) NSString *masterPasswordChoice;
@property(nonatomic, strong) NSOperationQueue *afterUpdates;
@property(nonatomic, weak) id contextChangedObserver;
@property(nonatomic, strong) NSFetchedResultsController *userResultsController;
@end
@@ -91,6 +91,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
[super viewWillAppear:animated];
self.userSelectionContainer.visible = NO;
[self.storeLoadingActivity startAnimating];
}
- (void)viewDidAppear:(BOOL)animated {
@@ -642,7 +643,6 @@ referenceSizeForFooterInSection:(NSInteger)section {
[self removeKeyPathObservers];
PearlRemoveNotificationObservers();
[[NSNotificationCenter defaultCenter] removeObserver:self.contextChangedObserver];
}
- (void)registerObservers {
@@ -674,24 +674,6 @@ referenceSizeForFooterInSection:(NSInteger)section {
[self.keyboardHeightConstraint updateConstant:keyboardHeight];
} );
if ((self.contextChangedObserver
= [[MPiOSAppDelegate get] managedObjectContextChanged:^(NSDictionary<NSManagedObjectID *, NSString *> *affectedObjects) {
if ([[[affectedObjects allKeys] filteredArrayUsingPredicate:
[NSPredicate predicateWithBlock:^BOOL(NSManagedObjectID *objectID, NSDictionary *bindings) {
return [objectID.entity.name isEqualToString:NSStringFromClass( [MPUserEntity class] )];
}]] count])
[self reloadUsers];
}]))
[UIView animateWithDuration:0.3f animations:^{
self.avatarCollectionView.visible = YES;
[self.storeLoadingActivity stopAnimating];
}];
else
[UIView animateWithDuration:0.3f animations:^{
self.avatarCollectionView.visible = NO;
[self.storeLoadingActivity startAnimating];
}];
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, [MPiOSAppDelegate get].storeCoordinator, nil,
^(MPUsersViewController *self, NSNotification *note) {
self.userIDs = nil;
@@ -703,32 +685,54 @@ referenceSizeForFooterInSection:(NSInteger)section {
[self reloadUsers];
} );
} );
[UIView animateWithDuration:0.3f animations:^{
self.avatarCollectionView.visible = YES;
[self.storeLoadingActivity stopAnimating];
}];
}
- (void)reloadUsers {
[self afterUpdatesMainQueue:^{
if (![MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
NSError *error = nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
fetchRequest.sortDescriptors = @[
[NSSortDescriptor sortDescriptorWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
];
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
if (!users) {
MPError( error, @"Failed to load users." );
self.userIDs = nil;
}
self.userResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:mainContext
sectionNameKeyPath:nil cacheName:nil];
self.userResultsController.delegate = self;
NSMutableArray *userIDs = [NSMutableArray arrayWithCapacity:[users count]];
for (MPUserEntity *user in users)
[userIDs addObject:user.permanentObjectID];
self.userIDs = userIDs;
NSError *error = nil;
if (![self.userResultsController performFetch:&error])
MPError( error, @"Failed to load users." );
[self updateUsers];
}])
self.userIDs = nil;
}];
}
- (void)updateUsers {
[self.userResultsController.managedObjectContext performBlock:^{
NSArray *users = self.userResultsController.fetchedObjects;
NSMutableArray *userIDs = [NSMutableArray arrayWithCapacity:[users count]];
for (MPUserEntity *user in users)
[userIDs addObject:user.permanentObjectID];
self.userIDs = userIDs;
}];
}
#pragma mark - NSFetchedResultsControllerDelegate
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self updateUsers];
}
#pragma mark - Properties
- (void)setActive:(BOOL)active animated:(BOOL)animated {

View File

@@ -98,12 +98,12 @@ decisionHandler:(void ( ^ )(WKNavigationActionPolicy))decisionHandler {
#pragma mark - Actions
- (IBAction)action:(id)sender {
- (IBAction)action:(UIBarButtonItem *)sender {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:self.webView.URL.host
message:self.webView.URL.absoluteString
preferredStyle:UIAlertControllerStyleActionSheet];
[controller.popoverPresentationController setSourceView:sender];
[controller.popoverPresentationController setBarButtonItem:sender];
[controller addAction:[UIAlertAction actionWithTitle:@"Safari" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[UIApp openURL:self.webView.URL];
}]];

View File

@@ -24,7 +24,7 @@
@interface MPiOSAppDelegate : MPAppDelegate_Shared <SKStoreProductViewControllerDelegate>
@property(nonatomic, strong) UIWindow *window;
@property(nonatomic, strong) SKStoreProductViewController *voltoViewController;
@property(nonatomic, strong) SKStoreProductViewController *spectreViewController;
- (void)openURL:(NSURL *)url;

View File

@@ -23,8 +23,10 @@
#import "mpw-marshal.h"
#import "MPSecrets.h"
MP_LIBS_BEGIN
#import <Sentry/Sentry.h>
#import <Countly/Countly.h>
MP_LIBS_END
@interface CountlyPushNotifications
@end
@@ -62,7 +64,7 @@
[SentrySDK startWithOptions:@{
@"dsn" : NilToNSNull( decrypt( sentryDSN ) ),
#ifdef DEBUG
@"debug" : @(YES),
@"debug" : @(NO), //@(YES),
@"environment" : @"Development",
#elif PUBLIC
@"debug" : @(NO),
@@ -122,7 +124,7 @@
countlyConfig.deviceID = [PearlKeyChain deviceIdentifier];
countlyConfig.secretSalt = decrypt( countlySalt );
#if DEBUG
countlyConfig.enableDebug = YES;
//countlyConfig.enableDebug = YES;
countlyConfig.pushTestMode = CLYPushTestModeDevelopment;
#elif ! PUBLIC
countlyConfig.enableDebug = NO;
@@ -187,16 +189,16 @@
SKStoreProductParameterCampaignToken : @"app-masterpassword.ios", /* Campaign: From MasterPassword iOS */
SKStoreProductParameterProviderToken : @153897, /* Provider: Maarten Billemont */
// SKStoreProductParameterITunesItemIdentifier: @510296984, /* Application: MasterPassword iOS */
SKStoreProductParameterITunesItemIdentifier: @1500430196, /* Application: Volto iOS */
SKStoreProductParameterITunesItemIdentifier: @1500430196, /* Application: Spectre iOS */
} completionBlock:^(BOOL result, NSError *error) {
if (error)
err( @"Failed loading Volto product information: %@", error );
err( @"Failed loading Spectre product information: %@", error );
if (result) {
self.voltoViewController = migrateVC;
self.voltoViewController.delegate = self;
self.spectreViewController = migrateVC;
self.spectreViewController.delegate = self;
} else {
self.voltoViewController = nil;
self.spectreViewController = nil;
}
}];
@@ -406,12 +408,6 @@
inf( @"Will foreground" );
[self.hangDetector start];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
inf( @"Re-activated" );
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
PearlNotMainQueue( ^{
NSString *importData = [UIPasteboard generalPasteboard].string;
@@ -434,6 +430,12 @@
} );
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
inf( @"Re-activated" );
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
inf( @"Received memory warning." );
@@ -705,7 +707,7 @@
- (void)migrateFor:(MPUserEntity *)user {
if ([UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto:"]]) {
if ([UIApp canOpenURL:[[NSURL alloc] initWithString:@"spectre:"]]) {
if (!user) {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
@@ -714,7 +716,7 @@
return;
UIAlertController *usersSheet = [UIAlertController alertControllerWithTitle:@"Migrate User"
message:@"Choose a user to migrate out to Volto."
message:@"Choose a user to migrate out to Spectre."
preferredStyle:UIAlertControllerStyleAlert];
[usersSheet addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
for (MPUserEntity *user_ in users)
@@ -762,7 +764,7 @@
return;
NSURLComponents *components = [NSURLComponents new];
components.scheme = @"volto";
components.scheme = @"spectre";
components.path = @"import";
components.queryItems = @[ [[NSURLQueryItem alloc] initWithName:@"data" value:exportedUser] ];
[UIApp openURL:components.URL];
@@ -770,8 +772,8 @@
}];
}
else if (self.voltoViewController)
[self.window.rootViewController presentViewController:self.voltoViewController animated:YES completion:nil];
else if (self.spectreViewController)
[self.window.rootViewController presentViewController:self.spectreViewController animated:YES completion:nil];
}
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset {

View File

@@ -60,7 +60,7 @@
<string>firefox</string>
<string>googlechrome</string>
<string>opera-http</string>
<string>volto</string>
<string>spectre</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="Q1S-vU-GGO">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17154" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="Q1S-vU-GGO">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17124"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<customFonts key="customFonts">
@@ -69,7 +69,7 @@
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPAvatarCell" id="Zab-uQ-uk9" customClass="MPAvatarCell">
<rect key="frame" x="80" y="115" width="215" height="667"/>
<rect key="frame" x="80" y="114.5" width="215" height="667"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="215" height="667"/>
@@ -164,7 +164,7 @@
<outlet property="delegate" destination="S8q-YF-Kt9" id="det-Eh-phM"/>
</connections>
</collectionView>
<button opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9u7-pu-Wtv" userLabel="Previous Avatar">
<button opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9u7-pu-Wtv" userLabel="Previous Avatar">
<rect key="frame" x="0.0" y="439.5" width="44" height="53"/>
<constraints>
<constraint firstAttribute="width" constant="44" id="Ay6-Jg-c3T"/>
@@ -177,7 +177,7 @@
<action selector="changeAvatar:" destination="S8q-YF-Kt9" eventType="touchUpInside" id="lNu-mK-3zD"/>
</connections>
</button>
<button opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fUK-gJ-NRE" userLabel="Next Avatar">
<button opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fUK-gJ-NRE" userLabel="Next Avatar">
<rect key="frame" x="370" y="439.5" width="44" height="53"/>
<constraints>
<constraint firstAttribute="width" constant="44" id="oAm-YX-Fx5"/>
@@ -276,7 +276,7 @@
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XEP-O3-ayG" userLabel="Footer">
<rect key="frame" x="0.0" y="824" width="414" height="72"/>
<subviews>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4md-Gp-SLG">
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4md-Gp-SLG">
<rect key="frame" x="20" y="48" width="374" height="24"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="10"/>
<state key="normal" title="Thanks, lhunath ➚">
@@ -798,7 +798,7 @@
<constraint firstAttribute="height" constant="110" id="zBf-EA-iDN"/>
</constraints>
</imageView>
<button opaque="NO" alpha="0.69999998807907104" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DzC-Ts-gew" userLabel="Previous Avatar">
<button opaque="NO" alpha="0.69999998807907104" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DzC-Ts-gew" userLabel="Previous Avatar">
<rect key="frame" x="20" y="148.5" width="44" height="53"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="44" id="1Wu-gS-flK"/>
@@ -812,7 +812,7 @@
<action selector="previousAvatar:" destination="JFc-sj-awD" eventType="touchUpInside" id="D92-6I-zFd"/>
</connections>
</button>
<button opaque="NO" alpha="0.69999998807907104" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yAf-fc-SKl" userLabel="Next Avatar">
<button opaque="NO" alpha="0.69999998807907104" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yAf-fc-SKl" userLabel="Next Avatar">
<rect key="frame" x="350" y="148.5" width="44" height="53"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="44" id="pEf-Vp-D7s"/>
@@ -1039,7 +1039,7 @@ Note that this feature requires you enable the Save Password option and have pur
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Rl7-cr-FHf">
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Rl7-cr-FHf">
<rect key="frame" x="20" y="118" width="374" height="27"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="12"/>
<state key="normal" title="Home Page">
@@ -1049,7 +1049,7 @@ Note that this feature requires you enable the Save Password option and have pur
<action selector="homePageButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="ptD-cv-NMr"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="epW-Rm-9St">
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="epW-Rm-9St">
<rect key="frame" x="20" y="153" width="374" height="27"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="12"/>
<state key="normal" title="Understanding Master Password's Security">
@@ -1059,7 +1059,7 @@ Note that this feature requires you enable the Save Password option and have pur
<action selector="securityButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="Efv-cp-Xfh"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LTN-ch-h8D">
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LTN-ch-h8D">
<rect key="frame" x="20" y="188" width="374" height="27"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="12"/>
<state key="normal" title="Get the Master Password source code">
@@ -1069,7 +1069,7 @@ Note that this feature requires you enable the Save Password option and have pur
<action selector="sourceButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="Y3O-di-CZo"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Z60-lc-Nka">
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Z60-lc-Nka">
<rect key="frame" x="20" y="223" width="374" height="27"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/>
<state key="normal" title="Send Thanks">
@@ -1675,16 +1675,16 @@ eg. apple.com, rmitchell@twitter.com</string>
<view opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3ve-eR-1IW">
<rect key="frame" x="0.0" y="740" width="414" height="156"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="PWC-I5-RSl">
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="PWC-I5-RSl">
<rect key="frame" x="8" y="8" width="398" height="106"/>
<state key="normal" backgroundImage="tip_alert_black"/>
<connections>
<action selector="upgradeVolto:" destination="nkY-z6-8jd" eventType="touchUpInside" id="8KJ-vm-3D0"/>
<action selector="upgradeSpectre:" destination="nkY-z6-8jd" eventType="touchUpInside" id="8KJ-vm-3D0"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XgC-Ky-oVC">
<rect key="frame" x="80" y="15" width="315" height="84"/>
<string key="text">The next generation of password security is now called «Volto»:
<string key="text">The next generation of password security is now called «Spectre»:
Tap to install the upgrade.
This app is now out of maintenance.</string>
@@ -1707,17 +1707,17 @@ This app is now out of maintenance.</string>
<view opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="67c-5R-Foa">
<rect key="frame" x="0.0" y="740" width="414" height="156"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="EZK-er-8ql">
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="EZK-er-8ql">
<rect key="frame" x="8" y="8" width="398" height="106"/>
<state key="normal" backgroundImage="tip_alert_black"/>
<connections>
<action selector="upgradeVolto:" destination="nkY-z6-8jd" eventType="touchUpInside" id="P4a-tL-9yX"/>
<action selector="upgradeSpectre:" destination="nkY-z6-8jd" eventType="touchUpInside" id="P4a-tL-9yX"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fLB-gA-32p">
<rect key="frame" x="80" y="15" width="315" height="84"/>
<string key="text">The next generation of password security is now called «Volto»:
Tap to copy this user into Volto.
<string key="text">The next generation of password security is now called «Spectre»:
Tap to copy this user into Spectre.
This app is now out of maintenance.</string>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
@@ -1816,8 +1816,8 @@ This app is now out of maintenance.</string>
<outlet property="popdownView" destination="XNM-XQ-rMe" id="FaW-4m-Fff"/>
<outlet property="searchBar" destination="aGs-1S-aC3" id="rTp-DP-rIz"/>
<outlet property="sitesToBottomConstraint" destination="dNt-uf-8BC" id="Ta6-eL-z7w"/>
<outlet property="voltoInstallAlert" destination="3ve-eR-1IW" id="Ah5-OX-XhQ"/>
<outlet property="voltoMigrateAlert" destination="67c-5R-Foa" id="aTi-fu-opO"/>
<outlet property="spectreInstallAlert" destination="3ve-eR-1IW" id="Ah5-OX-XhQ"/>
<outlet property="spectreMigrateAlert" destination="67c-5R-Foa" id="aTi-fu-opO"/>
<segue destination="z9O-w0-6oR" kind="modal" identifier="guide" id="Ql4-wf-T8u"/>
<segue destination="Foa-Er-RBr" kind="custom" identifier="message" customClass="MPOverlaySegue" id="Xne-Sm-HQt"/>
</connections>
@@ -1846,10 +1846,10 @@ This app is now out of maintenance.</string>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1lc-e7-Qme" userLabel="Emergency Generator">
<rect key="frame" x="20" y="240.5" width="374" height="415.5"/>
<rect key="frame" x="20" y="240" width="374" height="416"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Emergency Generator" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="4Lh-s0-Dbt">
<rect key="frame" x="20" y="20" width="334" height="21"/>
<rect key="frame" x="20" y="20" width="334" height="21.5"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
@@ -1857,7 +1857,7 @@ This app is now out of maintenance.</string>
<size key="shadowOffset" width="0.0" height="1"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Generate your password without logging in. Great for if you're borrowing a friend's device or are having trouble logging in." lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="vHS-3A-Tae">
<rect key="frame" x="20" y="49" width="334" height="51.5"/>
<rect key="frame" x="20" y="49.5" width="334" height="51.5"/>
<fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="14"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
@@ -1865,7 +1865,7 @@ This app is now out of maintenance.</string>
<size key="shadowOffset" width="0.0" height="1"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Your Name" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="XAC-Da-lpf" userLabel="User Name">
<rect key="frame" x="20" y="108.5" width="334" height="34"/>
<rect key="frame" x="20" y="109" width="334" height="34"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="words" keyboardAppearance="alert" returnKeyType="next" enablesReturnKeyAutomatically="YES"/>
<connections>
@@ -1874,7 +1874,7 @@ This app is now out of maintenance.</string>
</connections>
</textField>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Your Master Password" clearsOnBeginEditing="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="J46-0E-no3" userLabel="Master Password">
<rect key="frame" x="20" y="150.5" width="334" height="34"/>
<rect key="frame" x="20" y="151" width="334" height="34"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardAppearance="alert" returnKeyType="next" enablesReturnKeyAutomatically="YES" secureTextEntry="YES"/>
<connections>
@@ -1883,7 +1883,7 @@ This app is now out of maintenance.</string>
</connections>
</textField>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Site Name" clearsOnBeginEditing="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="56H-xR-09J" userLabel="Site Name">
<rect key="frame" x="20" y="192.5" width="334" height="34"/>
<rect key="frame" x="20" y="193" width="334" height="34"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="URL" keyboardAppearance="alert" returnKeyType="done" enablesReturnKeyAutomatically="YES"/>
<connections>
@@ -1892,7 +1892,7 @@ This app is now out of maintenance.</string>
</connections>
</textField>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bar" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="e4b-Iv-Pk9" userLabel="Type">
<rect key="frame" x="20" y="234.5" width="334" height="32"/>
<rect key="frame" x="20" y="235" width="334" height="32"/>
<segments>
<segment title="Max"/>
<segment title="Long"/>
@@ -1907,7 +1907,7 @@ This app is now out of maintenance.</string>
</connections>
</segmentedControl>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Counter" lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="cAo-K2-E23">
<rect key="frame" x="20" y="277.5" width="64" height="21.5"/>
<rect key="frame" x="20" y="278.5" width="64" height="21.5"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
@@ -1915,13 +1915,13 @@ This app is now out of maintenance.</string>
<size key="shadowOffset" width="0.0" height="1"/>
</label>
<stepper opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" maximumValue="100" translatesAutoresizingMaskIntoConstraints="NO" id="ZPT-EI-yuv" userLabel="Counter">
<rect key="frame" x="260" y="273.5" width="94" height="32"/>
<rect key="frame" x="260" y="274" width="94" height="32"/>
<connections>
<action selector="controlChanged:" destination="osn-5H-SWW" eventType="valueChanged" id="eQA-3X-uc9"/>
</connections>
</stepper>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="1" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="3Cd-XH-Wau">
<rect key="frame" x="245" y="278.5" width="7" height="20"/>
<rect key="frame" x="245" y="279" width="7" height="20.5"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@@ -1962,10 +1962,10 @@ This app is now out of maintenance.</string>
</state>
</button>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="4sN-hm-xio">
<rect key="frame" x="168.5" y="352" width="37" height="37"/>
<rect key="frame" x="168.5" y="352.5" width="37" height="37"/>
</activityIndicatorView>
<button opaque="NO" clipsSubviews="YES" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="clip" translatesAutoresizingMaskIntoConstraints="NO" id="bHR-he-dnZ">
<rect key="frame" x="20" y="345.5" width="334" height="50"/>
<rect key="frame" x="20" y="346" width="334" height="50"/>
<gestureRecognizers/>
<fontDescription key="fontDescription" name="SourceCodePro-Black" family="Source Code Pro" pointSize="30"/>
<state key="normal" title="XapaNuwjFihn6$">
@@ -1977,7 +1977,7 @@ This app is now out of maintenance.</string>
</connections>
</button>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="beo-cJ-jIn" userLabel="View - Content Tip">
<rect key="frame" x="82" y="310.5" width="210" height="60"/>
<rect key="frame" x="82" y="311" width="210" height="60"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black.png" translatesAutoresizingMaskIntoConstraints="NO" id="nyL-cO-aPa">
<rect key="frame" x="0.0" y="0.0" width="210" height="60"/>
@@ -2056,14 +2056,41 @@ This app is now out of maintenance.</string>
<constraint firstItem="1lc-e7-Qme" firstAttribute="leading" secondItem="whU-l0-2bU" secondAttribute="leading" constant="20" id="ztv-2f-RAc"/>
</constraints>
</scrollView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="cbe-hc-9K5">
<rect key="frame" x="20" y="820" width="374" height="42"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Anonymous Device Identifier" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="VyZ-qP-VkG">
<rect key="frame" x="0.0" y="0.0" width="374" height="18"/>
<fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="14"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
<color key="shadowColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<size key="shadowOffset" width="0.0" height="1"/>
</label>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DVF-Im-H2I">
<rect key="frame" x="0.0" y="18" width="374" height="24"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="10"/>
<state key="normal" title="0000000000000000000000000000000000000000">
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="copyDevice:" destination="osn-5H-SWW" eventType="touchUpInside" id="0dD-IS-Ktn"/>
</connections>
</button>
</subviews>
</stackView>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="whU-l0-2bU" firstAttribute="height" secondItem="GiS-3g-cDj" secondAttribute="height" multiplier="1:2" id="4oQ-JI-wQv"/>
<constraint firstAttribute="width" secondItem="whU-l0-2bU" secondAttribute="width" id="AiQ-LE-3bh"/>
<constraint firstItem="IV3-lc-Fnf" firstAttribute="top" secondItem="gRG-Ys-94p" secondAttribute="bottom" id="IMb-wl-Eeb"/>
<constraint firstItem="cbe-hc-9K5" firstAttribute="leading" secondItem="GiS-3g-cDj" secondAttribute="leadingMargin" id="KNZ-jb-npt"/>
<constraint firstAttribute="trailing" secondItem="gRG-Ys-94p" secondAttribute="trailing" id="Pb1-eY-0FG"/>
<constraint firstItem="IV3-lc-Fnf" firstAttribute="top" secondItem="cbe-hc-9K5" secondAttribute="bottom" id="YWv-H4-Ij3"/>
<constraint firstItem="gRG-Ys-94p" firstAttribute="top" secondItem="GiS-3g-cDj" secondAttribute="top" id="oyk-Xr-zTZ"/>
<constraint firstAttribute="trailingMargin" secondItem="cbe-hc-9K5" secondAttribute="trailing" id="wss-B8-PT6"/>
<constraint firstItem="gRG-Ys-94p" firstAttribute="leading" secondItem="GiS-3g-cDj" secondAttribute="leading" id="zhC-jf-5dY"/>
</constraints>
<userDefinedRuntimeAttributes>
@@ -2078,6 +2105,7 @@ This app is now out of maintenance.</string>
<outlet property="containerView" destination="GiS-3g-cDj" id="01o-PU-SNZ"/>
<outlet property="counterLabel" destination="3Cd-XH-Wau" id="wv7-jZ-6tX"/>
<outlet property="counterStepper" destination="ZPT-EI-yuv" id="w5c-hO-T51"/>
<outlet property="deviceButton" destination="DVF-Im-H2I" id="TMU-Vo-bDo"/>
<outlet property="dialogView" destination="1lc-e7-Qme" id="JYt-mv-XV2"/>
<outlet property="fullNameField" destination="XAC-Da-lpf" id="XCk-0H-IcI"/>
<outlet property="masterPasswordField" destination="J46-0E-no3" id="DfH-4n-cop"/>
@@ -2119,10 +2147,10 @@ This app is now out of maintenance.</string>
</items>
</navigationBar>
<pageControl opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" numberOfPages="3" translatesAutoresizingMaskIntoConstraints="NO" id="8A2-ly-WTX">
<rect key="frame" x="187.5" y="771" width="39" height="37"/>
<rect key="frame" x="141" y="778.5" width="132" height="29.5"/>
</pageControl>
<collectionView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" pagingEnabled="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" minimumZoomScale="0.0" maximumZoomScale="0.0" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="i2y-lo-HXR">
<rect key="frame" x="0.0" y="44" width="414" height="637"/>
<rect key="frame" x="0.0" y="44" width="414" height="644.5"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="m8O-kY-22j">
<size key="itemSize" width="320" height="458"/>
@@ -2132,7 +2160,7 @@ This app is now out of maintenance.</string>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPGuideStepCell" id="oGu-pJ-3bQ" customClass="MPGuideStepCell">
<rect key="frame" x="0.0" y="89.5" width="320" height="458"/>
<rect key="frame" x="0.0" y="93.5" width="320" height="458"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="320" height="458"/>
@@ -2161,13 +2189,13 @@ This app is now out of maintenance.</string>
</connections>
</collectionView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="To begin, tap the &quot;New User&quot; icon and add yourself as a user to the application." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ciw-56-nNy" userLabel="Caption">
<rect key="frame" x="8" y="689" width="398" height="82"/>
<rect key="frame" x="8" y="696.5" width="398" height="82"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="13"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label hidden="YES" opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Oop-Ff-gbz" userLabel="Caption Height Strut">
<rect key="frame" x="8" y="689" width="1" height="82"/>
<rect key="frame" x="8" y="696.5" width="1" height="82"/>
<string key="text" base64-UTF8="YES">
CgoKCgoKCgoKCgoKCg
</string>
@@ -2241,7 +2269,7 @@ Suspendisse potenti. Etiam ut nisi id augue tempor ultrices et sit amet sapien.
<dataDetectorType key="dataDetectorTypes" link="YES"/>
</textView>
<toolbar contentMode="scaleToFill" barStyle="black" translatesAutoresizingMaskIntoConstraints="NO" id="WmH-JB-jp2">
<rect key="frame" x="0.0" y="769" width="414" height="44"/>
<rect key="frame" x="0.0" y="764" width="414" height="49"/>
<items>
<barButtonItem systemItem="compose" id="BSV-3i-01h">
<connections>
@@ -2256,7 +2284,7 @@ Suspendisse potenti. Etiam ut nisi id augue tempor ultrices et sit amet sapien.
<barButtonItem systemItem="flexibleSpace" id="Lp4-ss-KxI"/>
<barButtonItem style="plain" id="aMS-HH-mnE">
<segmentedControl key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bar" selectedSegmentIndex="0" id="lxp-wx-uCy">
<rect key="frame" x="267" y="6" width="127" height="32"/>
<rect key="frame" x="267" y="11" width="127" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
<segment title="Normal"/>
@@ -2785,7 +2813,7 @@ Invested: 3.7 work hours</string>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" verticalCompressionResistancePriority="751" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lQ1-b9-Xp4">
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" verticalCompressionResistancePriority="751" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lQ1-b9-Xp4">
<rect key="frame" x="20" y="34" width="374" height="27"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="12"/>
<state key="normal" title="Restore Previous Purchases">
@@ -2795,7 +2823,7 @@ Invested: 3.7 work hours</string>
<action selector="restorePurchases:" destination="pdl-xv-zjX" eventType="touchUpInside" id="Q8M-ud-OHL"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eus-kQ-emn">
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eus-kQ-emn">
<rect key="frame" x="8" y="69" width="398" height="27"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/>
<state key="normal" title="Send Thanks">

View File

@@ -28,7 +28,7 @@ mpw_expect() {
json) file=~/.mpw.d/"$user.mpjson" ;;
esac
fi
[[ $file ]] && (( ! keep )) && rm "$file"
[[ -e $file ]] && (( ! keep )) && rm "$file"
printf '.'
local result=$(./mpw -q "${args[@]}") err=$?
@@ -79,397 +79,429 @@ mpw_expect() {
esac
fi
[[ $file ]] && (( ! keep )) && rm "$file"
[[ -e $file ]] && (( ! keep )) && rm "$file"
}
# mpw_tests.xml
## V3
printf "\nV%d, none: " 3
mpw_expect 'CefoTiciJuba7@' -Fnone \
-u 'test' -M 'test' 'test'
mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' 'ẗesẗ'
mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' -C '' 'ẗesẗ'
mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'KovxFipe5:Zatu' -Fnone \
-u '⛄' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'ModoLalhRapo6#' -Fnone \
-u 'tesẗ' -M '⛄' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'CudmTecuPune7:' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' '⛄'
mpw_expect 'yubfalago' -Fnone \
-u 'tesẗ' -M 'ẗest' -p 'identification' 'ẗesẗ'
mpw_expect 'yubfalago' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a3 -p 'identification' -C '' 'ẗesẗ'
mpw_expect 'jip nodwoqude dizo' -Fnone \
-u 'tesẗ' -M 'ẗest' -p 'recovery' 'ẗesẗ'
mpw_expect 'jip nodwoqude dizo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C '' 'ẗesẗ'
mpw_expect 'dok sorkicoyu ruya' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
mpw_expect 'j5TJ%G0WWwSMvYb)hr4)' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'TinRaz2?' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'jad0IQA3' -Fnone \
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Tin0' -Fnone \
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect '1710' -Fnone \
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'tinraziqu' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'tinr ziq taghuye zuj' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'HidiLonoFopt9&' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a3 -p 'authentication' 'ẗesẗ'
mpw_expect 'CefoTiciJuba7@' -Fnone \
-u 'test' -M 'test' 'test'
mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' 'ẗesẗ'
mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' -C '' 'ẗesẗ'
mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'KovxFipe5:Zatu' -Fnone \
-u '⛄' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'ModoLalhRapo6#' -Fnone \
-u 'tesẗ' -M '⛄' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'CudmTecuPune7:' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' '⛄'
mpw_expect 'yubfalago' -Fnone \
-u 'tesẗ' -M 'ẗest' -p 'identification' -C '' 'ẗesẗ'
mpw_expect 'yubfalago' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a3 -p 'identification' -C '' 'ẗesẗ'
mpw_expect 'jip nodwoqude dizo' -Fnone \
-u 'tesẗ' -M 'ẗest' -p 'recovery' -C '' 'ẗesẗ'
mpw_expect 'jip nodwoqude dizo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C '' 'ẗesẗ'
mpw_expect 'dok sorkicoyu ruya' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
mpw_expect 'j5TJ%G0WWwSMvYb)hr4)' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'TinRaz2?' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'jad0IQA3' -Fnone \
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Tin0' -Fnone \
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect '1710' -Fnone \
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'tinraziqu' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'tinr ziq taghuye zuj' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'HidiLonoFopt9&' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a3 -p 'authentication' -C '' 'ẗesẗ'
## V2
printf "\nV%d, none: " 2
mpw_expect 'CefoTiciJuba7@' -Fnone \
-u 'test' -M 'test' -tlong -c1 -a2 -p 'authentication' -C '' 'test'
mpw_expect "HuczFina3'Qatf" -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'SicrJuwaWaql0#' -Fnone \
-u '⛄' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'LokaJayp1@Faba' -Fnone \
-u 'tesẗ' -M '⛄' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'DoqaHulu8:Funh' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' '⛄'
mpw_expect 'yiyguxoxe' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a2 -p 'identification' -C '' 'ẗesẗ'
mpw_expect 'CefoTiciJuba7@' -Fnone \
-u 'test' -M 'test' -tlong -c1 -a2 -p 'authentication' -C '' 'test'
mpw_expect "HuczFina3'Qatf" -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'SicrJuwaWaql0#' -Fnone \
-u '⛄' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'LokaJayp1@Faba' -Fnone \
-u 'tesẗ' -M '⛄' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'DoqaHulu8:Funh' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' '⛄'
mpw_expect 'yiyguxoxe' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a2 -p 'identification' -C '' 'ẗesẗ'
mpw_expect 'vu yelyo bat kujavmu' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C '' 'ẗesẗ'
mpw_expect 'ka deqce xad vomacgi' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
mpw_expect 'wRF$LmB@umWGLWeVlB0-' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'HucZuk0!' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'wb59VoB5' -Fnone \
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Huc9' -Fnone \
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect '2959' -Fnone \
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'huczukamo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'huc finmokozi fota' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Mixa1~BulgNijo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a2 -p 'authentication' 'ẗesẗ'
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C '' 'ẗesẗ'
mpw_expect 'ka deqce xad vomacgi' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
mpw_expect 'wRF$LmB@umWGLWeVlB0-' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'HucZuk0!' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'wb59VoB5' -Fnone \
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Huc9' -Fnone \
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect '2959' -Fnone \
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'huczukamo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'huc finmokozi fota' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Mixa1~BulgNijo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a2 -p 'authentication' -C '' 'ẗesẗ'
## V1
printf "\nV%d, none: " 1
mpw_expect 'CefoTiciJuba7@' -Fnone \
-u 'test' -M 'test' -tlong -c1 -a1 -p 'authentication' -C '' 'test'
mpw_expect 'SuxiHoteCuwe3/' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'CupaTixu8:Hetu' -Fnone \
-u '⛄' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'NaqmBanu9+Decs' -Fnone \
-u 'tesẗ' -M '⛄' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'XowaDokoGeyu2)' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' '⛄'
mpw_expect 'makmabivo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a1 -p 'identification' -C '' 'ẗesẗ'
mpw_expect 'CefoTiciJuba7@' -Fnone \
-u 'test' -M 'test' -tlong -c1 -a1 -p 'authentication' -C '' 'test'
mpw_expect 'SuxiHoteCuwe3/' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'CupaTixu8:Hetu' -Fnone \
-u '⛄' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'NaqmBanu9+Decs' -Fnone \
-u 'tesẗ' -M '⛄' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'XowaDokoGeyu2)' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' '⛄'
mpw_expect 'makmabivo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a1 -p 'identification' -C '' 'ẗesẗ'
mpw_expect 'je mutbo buf puhiywo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C '' 'ẗesẗ'
mpw_expect 'ne hapfa dax qamayqo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
mpw_expect 'JlZo&eLhqgoxqtJ!NC5/' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'SuxHot2*' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Jly28Veh' -Fnone \
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Sux2' -Fnone \
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect '4922' -Fnone \
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'suxhotito' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'su hotte pav calewxo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Luxn2#JapiXopa' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a1 -p 'authentication' 'ẗesẗ'
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C '' 'ẗesẗ'
mpw_expect 'ne hapfa dax qamayqo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
mpw_expect 'JlZo&eLhqgoxqtJ!NC5/' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'SuxHot2*' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Jly28Veh' -Fnone \
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Sux2' -Fnone \
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect '4922' -Fnone \
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'suxhotito' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'su hotte pav calewxo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Luxn2#JapiXopa' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a1 -p 'authentication' -C '' 'ẗesẗ'
## V0
printf "\nV%d, none: " 0
mpw_expect 'GeqoBigiFubh2!' -Fnone \
-u 'test' -M 'test' -tlong -c1 -a0 -p 'authentication' -C '' 'test'
mpw_expect 'WumiZobxGuhe8]' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'KuhaXimj8@Zebu' -Fnone \
-u '⛄' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'CajtFayv9_Pego' -Fnone \
-u 'tesẗ' -M '⛄' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'QohaPokgYevu2!' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' '⛄'
mpw_expect 'takxabico' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a0 -p 'identification' -C '' 'ẗesẗ'
mpw_expect 'GeqoBigiFubh2!' -Fnone \
-u 'test' -M 'test' -tlong -c1 -a0 -p 'authentication' -C '' 'test'
mpw_expect 'WumiZobxGuhe8]' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'KuhaXimj8@Zebu' -Fnone \
-u '⛄' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'CajtFayv9_Pego' -Fnone \
-u 'tesẗ' -M '⛄' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'QohaPokgYevu2!' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' '⛄'
mpw_expect 'takxabico' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a0 -p 'identification' -C '' 'ẗesẗ'
mpw_expect 'je tuxfo fut huzivlo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C '' 'ẗesẗ'
mpw_expect 'ye zahqa lam jatavmo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
mpw_expect 'g4@)4SlA#)cJ#ib)vvH3' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Wum7_Xix' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'gAo78ARD' -Fnone \
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Wum7' -Fnone \
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect '9427' -Fnone \
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'wumdoxixo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'wu doxbe hac kaselqo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Pumy7.JadjQoda' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a0 -p 'authentication' 'ẗesẗ'
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C '' 'ẗesẗ'
mpw_expect 'ye zahqa lam jatavmo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
mpw_expect 'g4@)4SlA#)cJ#ib)vvH3' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Wum7_Xix' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'gAo78ARD' -Fnone \
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Wum7' -Fnone \
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect '9427' -Fnone \
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'wumdoxixo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'wu doxbe hac kaselqo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Pumy7.JadjQoda' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a0 -p 'authentication' -C '' 'ẗesẗ'
## V3
printf "\nV%d, flat: " 3
mpw_expect 'IfHuAUUpqpKZDZlNvz8$' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.max'
mpw_expect 'FamiJirk1)Zehc' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.long'
mpw_expect 'NofhMusw8+Cebo' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.⛄'
mpw_expect 'Necx1$LagaRizu' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tlong -c4294967295 -a3 -p 'authentication' 'ẗesẗ.c+a3pa'
mpw_expect 'Poq2)Tey' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.med'
mpw_expect 'Wr07Okx0' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.basic'
mpw_expect 'Bug9' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.short'
mpw_expect '3560' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.pin'
mpw_expect 'jupxiqepi' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.name'
mpw_expect 'vuh buxtukewo puhe' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.phrase'
mpw_expect 'mophabiwe' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi'
mpw_expect 'mup wulbezaxa juca' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C '' 'ẗesẗ.c1a3pr'
mpw_expect 'IfHuAUUpqpKZDZlNvz8$' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.max'
mpw_expect 'FamiJirk1)Zehc' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.long'
mpw_expect 'NofhMusw8+Cebo' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.⛄'
mpw_expect 'Necx1$LagaRizu' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -c4294967295 -a3 -p 'authentication' -C '' 'ẗesẗ.c+a3pa'
mpw_expect 'Poq2)Tey' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.med'
mpw_expect 'Wr07Okx0' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.basic'
mpw_expect 'Bug9' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.short'
mpw_expect '3560' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.pin'
mpw_expect 'jupxiqepi' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.name'
mpw_expect 'vuh buxtukewo puhe' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.phrase'
mpw_expect 'Cq5$TfH#OHmPS9yREp7)' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi.max'
mpw_expect 'mophabiwe' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi'
mpw_expect 'lA^ul!%9&TD%fj6icT1[' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'recovery' -C '' 'ẗesẗ.c1a3pr.max'
mpw_expect 'mup wulbezaxa juca' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -c1 -a3 -p 'recovery' -C '' 'ẗesẗ.c1a3pr'
mpw_expect 'molg rux kaczuvi ror' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a3pr.quesẗion'
-u 'tesẗ.v3' -M 'ẗest' -c1 -a3 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a3pr.quesẗion'
## V2
printf "\nV%d, flat: " 2
mpw_expect 'i7@0M*DdP4DgD#jJIzyL' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.max'
mpw_expect 'Lifw5]DablSuga' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.long'
mpw_expect 'Leja5%RavoZapa' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.⛄'
mpw_expect 'NejnGazo8?Seqo' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tlong -c4294967295 -a2 -p 'authentication' 'ẗesẗ.c+a2pa'
mpw_expect 'XicSux2&' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.med'
mpw_expect 'uEY50hcZ' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.basic'
mpw_expect 'Jif6' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.short'
mpw_expect '4001' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.pin'
mpw_expect 'rexmibace' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.name'
mpw_expect 'cez fexlemozo yula' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.phrase'
mpw_expect 'camfibeye' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi'
mpw_expect 'ye vemcu keq xepewmi' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C '' 'ẗesẗ.c1a2pr'
mpw_expect 'i7@0M*DdP4DgD#jJIzyL' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.max'
mpw_expect 'Lifw5]DablSuga' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.long'
mpw_expect 'Leja5%RavoZapa' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.⛄'
mpw_expect 'NejnGazo8?Seqo' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -c4294967295 -a2 -p 'authentication' -C '' 'ẗesẗ.c+a2pa'
mpw_expect 'XicSux2&' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.med'
mpw_expect 'uEY50hcZ' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.basic'
mpw_expect 'Jif6' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.short'
mpw_expect '4001' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.pin'
mpw_expect 'rexmibace' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.name'
mpw_expect 'cez fexlemozo yula' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.phrase'
mpw_expect 'T8+xi4NMd3HUGdV#GW*%' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi.max'
mpw_expect 'camfibeye' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi'
mpw_expect 'YLcoWeBwyiBf2*irFq1.' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'recovery' -C '' 'ẗesẗ.c1a2pr.max'
mpw_expect 'ye vemcu keq xepewmi' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -c1 -a2 -p 'recovery' -C '' 'ẗesẗ.c1a2pr'
mpw_expect 'yi qazne tid najuvme' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a2pr.quesẗion'
-u 'tesẗ.v2' -M 'ẗest' -c1 -a2 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a2pr.quesẗion'
## V1
printf "\nV%d, flat: " 1
mpw_expect 'a3~AiGkHk)Pgjbb)mk6H' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.max'
mpw_expect 'Lojz6?VotaJall' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.long'
mpw_expect 'Yoqu7)NiziFito' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.⛄'
mpw_expect 'Foha4[TojmXanc' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tlong -c4294967295 -a1 -p 'authentication' 'ẗesẗ.c+a1pa'
mpw_expect 'Hiy3*Zag' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.med'
mpw_expect 'UJR7HpG0' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.basic'
mpw_expect 'Cij7' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.short'
mpw_expect '0020' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.pin'
mpw_expect 'vadxovezu' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.name'
mpw_expect 'sij jihloyenu kizi' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.phrase'
mpw_expect 'qipberize' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi'
mpw_expect 'sok torxibute reza' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C '' 'ẗesẗ.c1a1pr'
mpw_expect 'a3~AiGkHk)Pgjbb)mk6H' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.max'
mpw_expect 'Lojz6?VotaJall' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.long'
mpw_expect 'Yoqu7)NiziFito' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.⛄'
mpw_expect 'Foha4[TojmXanc' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -c4294967295 -a1 -p 'authentication' -C '' 'ẗesẗ.c+a1pa'
mpw_expect 'Hiy3*Zag' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.med'
mpw_expect 'UJR7HpG0' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.basic'
mpw_expect 'Cij7' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.short'
mpw_expect '0020' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.pin'
mpw_expect 'vadxovezu' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.name'
mpw_expect 'sij jihloyenu kizi' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.phrase'
mpw_expect 'z2U9)(uQ78TXqtaus)8.' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi.max'
mpw_expect 'qipberize' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi'
mpw_expect 'QMciaKyi1&I*g%tHz99,' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'recovery' -C '' 'ẗesẗ.c1a1pr.max'
mpw_expect 'sok torxibute reza' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -c1 -a1 -p 'recovery' -C '' 'ẗesẗ.c1a1pr'
mpw_expect 'xacp qaw qutbece gan' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a1pr.quesẗion'
-u 'tesẗ.v1' -M 'ẗest' -c1 -a1 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a1pr.quesẗion'
## V0
printf "\nV%d, flat: " 0
mpw_expect 'b5@ww@Jmb4cAioRbivb)' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.max'
mpw_expect 'ZuceHazwLojz8!' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.long'
mpw_expect 'Boxj2!YabePodp' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.⛄'
mpw_expect 'PeblLuqc6]Cala' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tlong -c4294967295 -a0 -p 'authentication' 'ẗesẗ.c+a0pa'
mpw_expect 'XelQac0@' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.med'
mpw_expect 'qS07SRc8' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.basic'
mpw_expect 'Fih8' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.short'
mpw_expect '6121' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.pin'
mpw_expect 'rivfutipe' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.name'
mpw_expect 'xir qebdohogo buno' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.phrase'
mpw_expect 'ragcoxudo' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi'
mpw_expect 'kokl hov lowmaya xaf' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr'
mpw_expect 'wi zanmu nug zuwidwe' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a0pr.quesẗion'
mpw_expect 'b5@ww@Jmb4cAioRbivb)' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.max'
mpw_expect 'ZuceHazwLojz8!' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.long'
mpw_expect 'Boxj2!YabePodp' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.⛄'
mpw_expect 'PeblLuqc6]Cala' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -c4294967295 -a0 -p 'authentication' -C '' 'ẗesẗ.c+a0pa'
mpw_expect 'XelQac0@' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.med'
mpw_expect 'qS07SRc8' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.basic'
mpw_expect 'Fih8' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.short'
mpw_expect '6121' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.pin'
mpw_expect 'rivfutipe' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.name'
mpw_expect 'xir qebdohogo buno' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.phrase'
mpw_expect "RoAm3bJSvo@#loHSRA6\'" -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi.max'
mpw_expect 'ragcoxudo' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi'
mpw_expect 'm8]SiJHiAS@H@Rbw))34' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr.max'
mpw_expect 'kokl hov lowmaya xaf' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr'
mpw_expect 'wi zanmu nug zuwidwe' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a0pr.quesẗion'
## V3
printf "\nV%d, json: " 3
mpw_expect 'IfHuAUUpqpKZDZlNvz8$' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.max'
mpw_expect 'FamiJirk1)Zehc' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.long'
mpw_expect 'NofhMusw8+Cebo' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.⛄'
mpw_expect 'Necx1$LagaRizu' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tlong -c4294967295 -a3 -p 'authentication' 'ẗesẗ.c+a3pa'
mpw_expect 'Poq2)Tey' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.med'
mpw_expect 'Wr07Okx0' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.basic'
mpw_expect 'Bug9' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.short'
mpw_expect '3560' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.pin'
mpw_expect 'jupxiqepi' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.name'
mpw_expect 'vuh buxtukewo puhe' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.phrase'
mpw_expect 'mophabiwe' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi'
mpw_expect 'mup wulbezaxa juca' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C '' 'ẗesẗ.c1a3pr'
mpw_expect 'IfHuAUUpqpKZDZlNvz8$' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.max'
mpw_expect 'FamiJirk1)Zehc' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.long'
mpw_expect 'NofhMusw8+Cebo' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.⛄'
mpw_expect 'Necx1$LagaRizu' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -c4294967295 -a3 -p 'authentication' -C '' 'ẗesẗ.c+a3pa'
mpw_expect 'Poq2)Tey' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.med'
mpw_expect 'Wr07Okx0' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.basic'
mpw_expect 'Bug9' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.short'
mpw_expect '3560' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.pin'
mpw_expect 'jupxiqepi' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.name'
mpw_expect 'vuh buxtukewo puhe' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.phrase'
mpw_expect 'Cq5$TfH#OHmPS9yREp7)' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi.max'
mpw_expect 'mophabiwe' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi'
mpw_expect 'lA^ul!%9&TD%fj6icT1[' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'recovery' -C '' 'ẗesẗ.c1a3pr.max'
mpw_expect 'mup wulbezaxa juca' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -c1 -a3 -p 'recovery' -C '' 'ẗesẗ.c1a3pr'
mpw_expect 'molg rux kaczuvi ror' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a3pr.quesẗion'
-u 'tesẗ.v3' -M 'ẗest' -c1 -a3 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a3pr.quesẗion'
## V2
printf "\nV%d, json: " 2
mpw_expect 'i7@0M*DdP4DgD#jJIzyL' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.max'
mpw_expect 'Lifw5]DablSuga' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.long'
mpw_expect 'Leja5%RavoZapa' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.⛄'
mpw_expect 'NejnGazo8?Seqo' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tlong -c4294967295 -a2 -p 'authentication' 'ẗesẗ.c+a2pa'
mpw_expect 'XicSux2&' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.med'
mpw_expect 'uEY50hcZ' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.basic'
mpw_expect 'Jif6' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.short'
mpw_expect '4001' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.pin'
mpw_expect 'rexmibace' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.name'
mpw_expect 'cez fexlemozo yula' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.phrase'
mpw_expect 'camfibeye' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi'
mpw_expect 'ye vemcu keq xepewmi' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C '' 'ẗesẗ.c1a2pr'
mpw_expect 'i7@0M*DdP4DgD#jJIzyL' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.max'
mpw_expect 'Lifw5]DablSuga' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.long'
mpw_expect 'Leja5%RavoZapa' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.⛄'
mpw_expect 'NejnGazo8?Seqo' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -c4294967295 -a2 -p 'authentication' -C '' 'ẗesẗ.c+a2pa'
mpw_expect 'XicSux2&' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.med'
mpw_expect 'uEY50hcZ' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.basic'
mpw_expect 'Jif6' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.short'
mpw_expect '4001' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.pin'
mpw_expect 'rexmibace' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.name'
mpw_expect 'cez fexlemozo yula' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.phrase'
mpw_expect 'T8+xi4NMd3HUGdV#GW*%' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi.max'
mpw_expect 'camfibeye' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi'
mpw_expect 'YLcoWeBwyiBf2*irFq1.' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'recovery' -C '' 'ẗesẗ.c1a2pr.max'
mpw_expect 'ye vemcu keq xepewmi' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -c1 -a2 -p 'recovery' -C '' 'ẗesẗ.c1a2pr'
mpw_expect 'yi qazne tid najuvme' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a2pr.quesẗion'
-u 'tesẗ.v2' -M 'ẗest' -c1 -a2 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a2pr.quesẗion'
## V1
printf "\nV%d, json: " 1
mpw_expect 'a3~AiGkHk)Pgjbb)mk6H' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.max'
mpw_expect 'Lojz6?VotaJall' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.long'
mpw_expect 'Yoqu7)NiziFito' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.⛄'
mpw_expect 'Foha4[TojmXanc' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tlong -c4294967295 -a1 -p 'authentication' 'ẗesẗ.c+a1pa'
mpw_expect 'Hiy3*Zag' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.med'
mpw_expect 'UJR7HpG0' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.basic'
mpw_expect 'Cij7' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.short'
mpw_expect '0020' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.pin'
mpw_expect 'vadxovezu' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.name'
mpw_expect 'sij jihloyenu kizi' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.phrase'
mpw_expect 'qipberize' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi'
mpw_expect 'sok torxibute reza' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C '' 'ẗesẗ.c1a1pr'
mpw_expect 'a3~AiGkHk)Pgjbb)mk6H' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.max'
mpw_expect 'Lojz6?VotaJall' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.long'
mpw_expect 'Yoqu7)NiziFito' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.⛄'
mpw_expect 'Foha4[TojmXanc' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -c4294967295 -a1 -p 'authentication' -C '' 'ẗesẗ.c+a1pa'
mpw_expect 'Hiy3*Zag' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.med'
mpw_expect 'UJR7HpG0' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.basic'
mpw_expect 'Cij7' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.short'
mpw_expect '0020' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.pin'
mpw_expect 'vadxovezu' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.name'
mpw_expect 'sij jihloyenu kizi' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.phrase'
mpw_expect 'z2U9)(uQ78TXqtaus)8.' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi.max'
mpw_expect 'qipberize' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi'
mpw_expect 'QMciaKyi1&I*g%tHz99,' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'recovery' -C '' 'ẗesẗ.c1a1pr.max'
mpw_expect 'sok torxibute reza' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -c1 -a1 -p 'recovery' -C '' 'ẗesẗ.c1a1pr'
mpw_expect 'xacp qaw qutbece gan' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a1pr.quesẗion'
-u 'tesẗ.v1' -M 'ẗest' -c1 -a1 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a1pr.quesẗion'
## V0
printf "\nV%d, json: " 0
mpw_expect 'b5@ww@Jmb4cAioRbivb)' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.max'
mpw_expect 'ZuceHazwLojz8!' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.long'
mpw_expect 'Boxj2!YabePodp' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.⛄'
mpw_expect 'PeblLuqc6]Cala' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tlong -c4294967295 -a0 -p 'authentication' 'ẗesẗ.c+a0pa'
mpw_expect 'XelQac0@' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.med'
mpw_expect 'qS07SRc8' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.basic'
mpw_expect 'Fih8' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.short'
mpw_expect '6121' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.pin'
mpw_expect 'rivfutipe' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.name'
mpw_expect 'xir qebdohogo buno' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.phrase'
mpw_expect 'ragcoxudo' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi'
mpw_expect 'kokl hov lowmaya xaf' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr'
mpw_expect 'wi zanmu nug zuwidwe' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a0pr.quesẗion'
mpw_expect 'b5@ww@Jmb4cAioRbivb)' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.max'
mpw_expect 'ZuceHazwLojz8!' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.long'
mpw_expect 'Boxj2!YabePodp' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.⛄'
mpw_expect 'PeblLuqc6]Cala' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -c4294967295 -a0 -p 'authentication' -C '' 'ẗesẗ.c+a0pa'
mpw_expect 'XelQac0@' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.med'
mpw_expect 'qS07SRc8' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.basic'
mpw_expect 'Fih8' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.short'
mpw_expect '6121' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.pin'
mpw_expect 'rivfutipe' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.name'
mpw_expect 'xir qebdohogo buno' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.phrase'
mpw_expect "RoAm3bJSvo@#loHSRA6\'" -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi.max'
mpw_expect 'ragcoxudo' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi'
mpw_expect 'm8]SiJHiAS@H@Rbw))34' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr.max'
mpw_expect 'kokl hov lowmaya xaf' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr'
mpw_expect 'wi zanmu nug zuwidwe' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a0pr.quesẗion'
# Finish

View File

@@ -166,6 +166,11 @@ const char *mpw_site_crypted_password_v0(
err( "Missing encrypted state." );
return NULL;
}
if (strlen( cipherText ) % 4 != 0) {
wrn( "Malformed encrypted state, not base64." );
// This can happen if state was stored in a non-encrypted form, eg. login in old mpsites.
return strdup( cipherText );
}
// Base64-decode
uint8_t *cipherBuf = calloc( 1, mpw_base64_decode_max( cipherText ) );
@@ -184,8 +189,10 @@ const char *mpw_site_crypted_password_v0(
mpw_free( &plainBytes, bufSize );
if (!plainText)
err( "AES decryption error: %s", strerror( errno ) );
else if (!mpw_utf8_strchars( plainText ))
wrn( "decrypted -> plainText: %zu bytes = illegal UTF-8 = %s", strlen( plainText ), mpw_hex( plainText, bufSize ) );
else
trc( "decrypted -> plainText: %zu bytes = %s = %s", strlen( plainText ), plainText, mpw_hex( plainText, strlen( plainText ) ) );
trc( "decrypted -> plainText: %zu bytes = %s = %s", strlen( plainText ), plainText, mpw_hex( plainText, bufSize ) );
return plainText;
}

View File

@@ -980,7 +980,7 @@ static void mpw_marshal_read_flat(
str_counter = mpw_strdup( strtok( NULL, "" ) );
mpw_free_string( &typeAndVersionAndCounter );
}
siteLoginState = mpw_get_token( &positionInLine, endOfLine, "\t\n" ); // TODO: Needs to be encoded if redacted?
siteLoginState = mpw_get_token( &positionInLine, endOfLine, "\t\n" );
siteName = mpw_get_token( &positionInLine, endOfLine, "\t\n" );
siteResultState = mpw_get_token( &positionInLine, endOfLine, "\n" );
break;
@@ -1019,11 +1019,9 @@ static void mpw_marshal_read_flat(
mpw_marshal_data_set_num( siteCounter, file->data, "sites", siteName, "counter", NULL );
mpw_marshal_data_set_num( siteAlgorithm, file->data, "sites", siteName, "algorithm", NULL );
mpw_marshal_data_set_num( siteType, file->data, "sites", siteName, "type", NULL );
mpw_marshal_data_set_str( siteResultState && strlen( siteResultState )? siteResultState: NULL, file->data, "sites", siteName,
"password", NULL );
mpw_marshal_data_set_str( siteResultState, file->data, "sites", siteName, "password", NULL );
mpw_marshal_data_set_num( MPResultTypeDefault, file->data, "sites", siteName, "login_type", NULL );
mpw_marshal_data_set_str( siteLoginState && strlen( siteLoginState )? siteLoginState: NULL, file->data, "sites", siteName,
"login_name", NULL );
mpw_marshal_data_set_str( siteLoginState, file->data, "sites", siteName, "login_name", NULL );
mpw_marshal_data_set_num( strtol( str_uses, NULL, 10 ), file->data, "sites", siteName, "uses", NULL );
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &siteLastUsed ) ))
mpw_marshal_data_set_str( dateString, file->data, "sites", siteName, "last_used", NULL );

View File

@@ -223,6 +223,7 @@ const char *mpw_marshal_write(
MPMarshalledFile *mpw_marshal_read(
MPMarshalledFile *file, const char *in);
/** Authenticate as the user identified by the given marshalled file.
* @note This object stores a reference to the given key provider.
* @return A user object (allocated), or NULL if the file format provides no marshalling or a format error occurred. */
MPMarshalledUser *mpw_marshal_auth(
MPMarshalledFile *file, const MPMasterKeyProvider masterKeyProvider);
@@ -230,19 +231,25 @@ MPMarshalledUser *mpw_marshal_auth(
//// Creating.
/** Create a new user object ready for marshalling.
* @note This object stores copies of the strings assigned to it and manages their deallocation internally.
* @return A user object (allocated), or NULL if the fullName is missing or the marshalled user couldn't be allocated. */
MPMarshalledUser *mpw_marshal_user(
const char *fullName, const MPMasterKeyProvider masterKeyProvider, const MPAlgorithmVersion algorithmVersion);
/** Create a new site attached to the given user object, ready for marshalling.
* @note This object stores copies of the strings assigned to it and manages their deallocation internally.
* @return A site object (allocated), or NULL if the siteName is missing or the marshalled site couldn't be allocated. */
MPMarshalledSite *mpw_marshal_site(
MPMarshalledUser *user,
const char *siteName, const MPResultType resultType, const MPCounterValue siteCounter, const MPAlgorithmVersion algorithmVersion);
/** Create a new question attached to the given site object, ready for marshalling.
* @note This object stores copies of the strings assigned to it and manages their deallocation internally.
* @return A question object (allocated), or NULL if the marshalled question couldn't be allocated. */
MPMarshalledQuestion *mpw_marshal_question(
MPMarshalledSite *site, const char *keyword);
/** Create or update a marshal file descriptor.
* @param file If NULL, a new file will be allocated. Otherwise, the given file will be updated and the updated file returned.
* @param info If NULL, the file's info will be left as-is, otherwise it will be replaced by the given one. The file will manage the info's deallocation.
* @param data If NULL, the file's data will be left as-is, otherwise it will be replaced by the given one. The file will manage the data's deallocation.
* @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);

View File

@@ -607,6 +607,9 @@ const uint8_t *mpw_unhex(const char *hex, size_t *length) {
size_t mpw_utf8_charlen(const char *utf8String) {
if (!utf8String)
return 0;
// Legal UTF-8 byte sequences: <http://www.unicode.org/unicode/uni2errata/UTF-8_Corrigendum.html>
unsigned char utf8Char = (unsigned char)*utf8String;
if (utf8Char >= 0x00 && utf8Char <= 0x7F)
@@ -624,8 +627,9 @@ size_t mpw_utf8_charlen(const char *utf8String) {
size_t mpw_utf8_strchars(const char *utf8String) {
size_t strchars = 0, charlen;
for (char *remaining = (char *)utf8String; (charlen = mpw_utf8_charlen( remaining )); remaining += charlen)
++strchars;
for (char *remaining = (char *)utf8String; remaining && *remaining; remaining += charlen, ++strchars)
if (!(charlen = mpw_utf8_charlen( remaining )))
return 0;
return strchars;
}

View File

@@ -256,9 +256,9 @@ bool mpw_id_buf_equals(MPKeyID id1, MPKeyID id2);
//// String utilities.
/** @return The byte length of the UTF-8 character at the start of the given string. */
/** @return The byte length of the UTF-8 character at the start of the given string or 0 if it is NULL, empty or not a legal UTF-8 character. */
size_t mpw_utf8_charlen(const char *utf8String);
/** @return The amount of UTF-8 characters in the given string. */
/** @return The amount of UTF-8 characters in the given string or 0 if it is NULL, empty, or contains bytes that are not legal in UTF-8. */
size_t mpw_utf8_strchars(const char *utf8String);
/** Drop-in for memdup(3).
* @return A buffer (allocated, len) with len bytes copied from src or NULL if src is missing or the buffer could not be allocated. */