2
0

Huge refactor to do marshalling completely through mpw data object now.

This commit is contained in:
Maarten Billemont 2019-10-02 23:16:20 -04:00
parent 0fcdb2a5e6
commit 44fa32697a
6 changed files with 388 additions and 637 deletions

View File

@ -565,7 +565,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
saveInContext:(NSManagedObjectContext *)context { saveInContext:(NSManagedObjectContext *)context {
// Read metadata for the import file. // Read metadata for the import file.
MPMarshalledInfo *info = mpw_marshal_read_info( importData.UTF8String ); MPMarshalledInfo *info = mpw_marshal_read( importData.UTF8String );
if (info->format == MPMarshalFormatNone) if (info->format == MPMarshalFormatNone)
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{ return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
@"type" : @(MPMarshalErrorFormat), @"type" : @(MPMarshalErrorFormat),
@ -589,7 +589,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
// Parse import data. // Parse import data.
MPMarshalError importError = { .type = MPMarshalSuccess }; MPMarshalError importError = { .type = MPMarshalSuccess };
MPMarshalledUser *importUser = mpw_marshal_read( importData.UTF8String, info->format, importMasterPassword.UTF8String, &importError ); MPMarshalledUser *importUser = mpw_marshal_auth( importData.UTF8String, info->format, importMasterPassword.UTF8String, &importError );
mpw_marshal_info_free( &info ); mpw_marshal_info_free( &info );
@try { @try {

View File

@ -157,6 +157,7 @@ typedef struct {
const char *resultState; const char *resultState;
const char *resultParam; const char *resultParam;
MPMarshalledFile *file; MPMarshalledFile *file;
MPMarshalledUser *user;
MPMarshalledSite *site; MPMarshalledSite *site;
MPMarshalledQuestion *question; MPMarshalledQuestion *question;
} Operation; } Operation;
@ -233,8 +234,8 @@ int main(const int argc, char *const argv[]) {
// Operation summary. // Operation summary.
dbg( "-----------------" ); dbg( "-----------------" );
if (operation.file && operation.file->user) { if (operation.file && operation.user) {
dbg( "fullName : %s", operation.file->user->fullName ); dbg( "fullName : %s", operation.user->fullName );
dbg( "identicon : %s", operation.identicon ); dbg( "identicon : %s", operation.identicon );
dbg( "sitesFormat : %s%s", mpw_format_name( operation.sitesFormat ), operation.sitesFormatFixed? " (fixed)": "" ); dbg( "sitesFormat : %s%s", mpw_format_name( operation.sitesFormat ), operation.sitesFormatFixed? " (fixed)": "" );
dbg( "sitesPath : %s", operation.sitesPath ); dbg( "sitesPath : %s", operation.sitesPath );
@ -273,6 +274,7 @@ void cli_free(Arguments *args, Operation *operation) {
mpw_free_strings( &operation->keyContext, &operation->resultState, &operation->resultParam, NULL ); mpw_free_strings( &operation->keyContext, &operation->resultState, &operation->resultParam, NULL );
mpw_free_strings( &operation->identicon, &operation->sitesPath, NULL ); mpw_free_strings( &operation->identicon, &operation->sitesPath, NULL );
mpw_marshal_file_free( &operation->file ); mpw_marshal_file_free( &operation->file );
mpw_marshal_user_free( &operation->user );
operation->site = NULL; operation->site = NULL;
operation->question = NULL; operation->question = NULL;
cli_masterKeyProvider_free(); cli_masterKeyProvider_free();
@ -488,8 +490,9 @@ void cli_user(Arguments *args, Operation *operation) {
// If no user from mpsites, create a new one. // If no user from mpsites, create a new one.
mpw_free_string( &operation->sitesPath ); mpw_free_string( &operation->sitesPath );
mpw_marshal_file_free( &operation->file ); mpw_marshal_file_free( &operation->file );
operation->file = mpw_marshal_file( NULL, mpw_marshal_user( mpw_marshal_user_free( &operation->user );
operation->fullName, cli_masterKeyProvider_op( operation ), MPAlgorithmVersionCurrent ), NULL ); operation->file = mpw_marshal_file( NULL, NULL, NULL );
operation->user = mpw_marshal_user( operation->fullName, cli_masterKeyProvider_op( operation ), MPAlgorithmVersionCurrent );
} }
else { else {
@ -500,42 +503,44 @@ void cli_user(Arguments *args, Operation *operation) {
fclose( sitesFile ); fclose( sitesFile );
// Parse file. // Parse file.
MPMarshalError marshalError = { .type = MPMarshalSuccess };
mpw_marshal_file_free( &operation->file ); mpw_marshal_file_free( &operation->file );
operation->file = mpw_marshal_read( sitesInputData, mpw_marshal_user_free( &operation->user );
cli_masterKeyProvider_op( operation ), &marshalError ); operation->file = mpw_marshal_read( NULL, sitesInputData );
if (marshalError.type == MPMarshalErrorMasterPassword && operation->allowPasswordUpdate) { if (operation->file && operation->file->error.type == MPMarshalSuccess) {
// Update master password in mpsites. operation->user = mpw_marshal_auth( operation->file, cli_masterKeyProvider_op( operation ) );
while (marshalError.type == MPMarshalErrorMasterPassword) {
inf( "Given master password does not match configuration." );
inf( "To update the configuration with this new master password, first confirm the old master password." );
const char *importMasterPassword = NULL; if (operation->file->error.type == MPMarshalErrorMasterPassword && operation->allowPasswordUpdate) {
while (!importMasterPassword || !strlen( importMasterPassword )) { // Update master password in mpsites.
while (operation->file->error.type == MPMarshalErrorMasterPassword) {
inf( "Given master password does not match configuration." );
inf( "To update the configuration with this new master password, first confirm the old master password." );
const char *importMasterPassword = NULL;
while (!importMasterPassword || !strlen( importMasterPassword )) {
mpw_free_string( &importMasterPassword );
importMasterPassword = mpw_getpass( "Old master password: " );
}
mpw_marshal_user_free( &operation->user );
operation->user = mpw_marshal_auth( operation->file, cli_masterKeyProvider_str( importMasterPassword ) );
if (operation->file && operation->user)
operation->user->masterKeyProvider = cli_masterKeyProvider_op( operation );
mpw_free_string( &importMasterPassword ); mpw_free_string( &importMasterPassword );
importMasterPassword = mpw_getpass( "Old master password: " );
} }
mpw_marshal_file_free( &operation->file );
operation->file = mpw_marshal_read( sitesInputData,
cli_masterKeyProvider_str( importMasterPassword ), &marshalError );
if (operation->file && operation->file->user)
operation->file->user->masterKeyProvider = cli_masterKeyProvider_op( operation );
mpw_free_string( &importMasterPassword );
} }
} }
mpw_free_string( &sitesInputData ); mpw_free_string( &sitesInputData );
// Incorrect master password. // Incorrect master password.
if (marshalError.type == MPMarshalErrorMasterPassword) { if (operation->file->error.type == MPMarshalErrorMasterPassword) {
ftl( "Incorrect master password according to configuration:\n %s: %s", operation->sitesPath, marshalError.message ); ftl( "Incorrect master password according to configuration:\n %s: %s", operation->sitesPath, operation->file->error.message );
cli_free( args, operation ); cli_free( args, operation );
exit( EX_DATAERR ); exit( EX_DATAERR );
} }
// Any other parse error. // Any other parse error.
if (!operation->file || !operation->file->user || marshalError.type != MPMarshalSuccess) { if (!operation->file || !operation->user || operation->file->error.type != MPMarshalSuccess) {
err( "Couldn't parse configuration file:\n %s: %s", operation->sitesPath, marshalError.message ); err( "Couldn't parse configuration file:\n %s: %s", operation->sitesPath, operation->file->error.message );
cli_free( args, operation ); cli_free( args, operation );
exit( EX_DATAERR ); exit( EX_DATAERR );
} }
@ -548,7 +553,7 @@ void cli_site(Arguments *args, Operation *operation) {
abort(); abort();
// Load the site object from mpsites. // Load the site object from mpsites.
MPMarshalledUser *user = operation->file->user; MPMarshalledUser *user = operation->user;
for (size_t s = 0; !operation->site && s < user->sites_count; ++s) for (size_t s = 0; !operation->site && s < user->sites_count; ++s)
if (strcmp( operation->siteName, (&user->sites[s])->siteName ) == OK) if (strcmp( operation->siteName, (&user->sites[s])->siteName ) == OK)
operation->site = &user->sites[s]; operation->site = &user->sites[s];
@ -588,8 +593,8 @@ void cli_question(Arguments *args, Operation *operation) {
void cli_operation(Arguments *args, Operation *operation) { void cli_operation(Arguments *args, Operation *operation) {
mpw_free_string( &operation->identicon ); mpw_free_string( &operation->identicon );
operation->file->user->identicon = mpw_identicon( operation->file->user->fullName, operation->masterPassword ); operation->user->identicon = mpw_identicon( operation->user->fullName, operation->masterPassword );
operation->identicon = mpw_identicon_render( operation->file->user->identicon ); operation->identicon = mpw_identicon_render( operation->user->identicon );
if (!operation->site) if (!operation->site)
abort(); abort();
@ -703,9 +708,9 @@ void cli_algorithmVersion(Arguments *args, Operation *operation) {
void cli_sitesRedacted(Arguments *args, Operation *operation) { void cli_sitesRedacted(Arguments *args, Operation *operation) {
if (args->sitesRedacted) if (args->sitesRedacted)
operation->file->user->redacted = strcmp( args->sitesRedacted, "1" ) == OK; operation->user->redacted = strcmp( args->sitesRedacted, "1" ) == OK;
else if (!operation->file->user->redacted) else if (!operation->user->redacted)
wrn( "Sites configuration is not redacted. Use -R 1 to change this." ); wrn( "Sites configuration is not redacted. Use -R 1 to change this." );
} }
@ -716,21 +721,21 @@ void cli_mpw(Arguments *args, Operation *operation) {
if (mpw_verbosity >= LogLevelInfo) if (mpw_verbosity >= LogLevelInfo)
fprintf( stderr, "%s's %s for %s:\n[ %s ]: ", fprintf( stderr, "%s's %s for %s:\n[ %s ]: ",
operation->file->user->fullName, operation->purposeResult, operation->site->siteName, operation->identicon ); operation->user->fullName, operation->purposeResult, operation->site->siteName, operation->identicon );
// Determine master key. // Determine master key.
MPMasterKey masterKey = NULL; MPMasterKey masterKey = NULL;
if (operation->file->user->masterKeyProvider) if (operation->user->masterKeyProvider)
masterKey = operation->file->user->masterKeyProvider( operation->site->algorithm, operation->file->user->fullName ); masterKey = operation->user->masterKeyProvider( operation->site->algorithm, operation->user->fullName );
if (!masterKey) { if (!masterKey) {
ftl( "Couldn't derive master key." ); ftl( "Couldn't derive master key." );
cli_free( args, operation ); cli_free( args, operation );
exit( EX_SOFTWARE ); exit( EX_SOFTWARE );
} }
MPKeyID keyID = mpw_id_buf( masterKey, MPMasterKeySize ); MPKeyID keyID = mpw_id_buf( masterKey, MPMasterKeySize );
if (!operation->file->user->keyID) if (!operation->user->keyID)
operation->file->user->keyID = mpw_strdup( keyID ); operation->user->keyID = mpw_strdup( keyID );
else if (!mpw_id_buf_equals( keyID, operation->file->user->keyID )) { else if (!mpw_id_buf_equals( keyID, operation->user->keyID )) {
ftl( "Master key mismatch." ); ftl( "Master key mismatch." );
cli_free( args, operation ); cli_free( args, operation );
exit( EX_SOFTWARE ); exit( EX_SOFTWARE );
@ -792,12 +797,15 @@ void cli_mpw(Arguments *args, Operation *operation) {
mpw_free_string( &result ); mpw_free_string( &result );
// Update usage metadata. // Update usage metadata.
operation->site->lastUsed = operation->file->user->lastUsed = time( NULL ); operation->site->lastUsed = operation->user->lastUsed = time( NULL );
operation->site->uses++; operation->site->uses++;
} }
void cli_save(Arguments *args, Operation *operation) { void cli_save(Arguments *args, Operation *operation) {
if (!operation->file || !operation->user)
return;
if (!operation->sitesFormatFixed) if (!operation->sitesFormatFixed)
operation->sitesFormat = MPMarshalFormatDefault; operation->sitesFormat = MPMarshalFormatDefault;
@ -807,7 +815,7 @@ void cli_save(Arguments *args, Operation *operation) {
return; return;
mpw_free_string( &operation->sitesPath ); mpw_free_string( &operation->sitesPath );
operation->sitesPath = mpw_path( operation->file->user->fullName, extensions[0] ); operation->sitesPath = mpw_path( operation->user->fullName, extensions[0] );
dbg( "Updating: %s (%s)", operation->sitesPath, mpw_format_name( operation->sitesFormat ) ); dbg( "Updating: %s (%s)", operation->sitesPath, mpw_format_name( operation->sitesFormat ) );
mpw_free( &extensions, count * sizeof( *extensions ) ); mpw_free( &extensions, count * sizeof( *extensions ) );
@ -817,10 +825,9 @@ void cli_save(Arguments *args, Operation *operation) {
return; return;
} }
MPMarshalError marshalError = { .type = MPMarshalSuccess }; const char *buf = mpw_marshal_write( operation->sitesFormat, operation->file, operation->user );
const char *buf = mpw_marshal_write( operation->sitesFormat, operation->file, &marshalError ); if (!buf || operation->file->error.type != MPMarshalSuccess)
if (!buf || marshalError.type != MPMarshalSuccess) wrn( "Couldn't encode updated configuration file:\n %s: %s", operation->sitesPath, operation->file->error.message );
wrn( "Couldn't encode updated configuration file:\n %s: %s", operation->sitesPath, marshalError.message );
else if (fwrite( buf, sizeof( char ), strlen( buf ), sitesFile ) != strlen( buf )) else if (fwrite( buf, sizeof( char ), strlen( buf ), sitesFile ) != strlen( buf ))
wrn( "Error while writing updated configuration file:\n %s: %d", operation->sitesPath, ferror( sitesFile ) ); wrn( "Error while writing updated configuration file:\n %s: %d", operation->sitesPath, ferror( sitesFile ) );

View File

@ -254,7 +254,7 @@ const char *mpw_identicon_encode(
const MPIdenticon identicon) { const MPIdenticon identicon) {
if (identicon.color == MPIdenticonColorUnset) if (identicon.color == MPIdenticonColorUnset)
return ""; return NULL;
return mpw_str( "%hhu:%s%s%s%s", return mpw_str( "%hhu:%s%s%s%s",
identicon.color, identicon.leftArm, identicon.body, identicon.rightArm, identicon.accessory ); identicon.color, identicon.leftArm, identicon.body, identicon.rightArm, identicon.accessory );

View File

@ -68,7 +68,7 @@ const char *mpw_site_state(
/** @return An identicon (static) that represents the user's identity. */ /** @return An identicon (static) that represents the user's identity. */
const MPIdenticon mpw_identicon( const MPIdenticon mpw_identicon(
const char *fullName, const char *masterPassword); const char *fullName, const char *masterPassword);
/** @return An encoded representation (shared) of the given identicon or an empty string if the identicon is unset. */ /** @return An encoded representation (shared) of the given identicon or NULL if the identicon is unset. */
const char *mpw_identicon_encode( const char *mpw_identicon_encode(
const MPIdenticon identicon); const MPIdenticon identicon);
/** @return An identicon (static) decoded from the given encoded identicon representation or an identicon with empty fields if the identicon could not be parsed. */ /** @return An identicon (static) decoded from the given encoded identicon representation or an identicon with empty fields if the identicon could not be parsed. */

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,9 @@ MP_LIBS_END
//// Types. //// Types.
#define mpw_default( __default, __value ) ({ __typeof__ (__default) _v = (__typeof__ (__default))(__value); _v = _v? _v: __default; })
#define mpw_default_n( __default, __num ) ({ __typeof__ (__default) _n = (__typeof__ (__default))(__num); _n = !isnan( _n )? _n: __default; })
typedef mpw_enum( unsigned int, MPMarshalFormat ) { typedef mpw_enum( unsigned int, MPMarshalFormat ) {
/** Do not marshal. */ /** Do not marshal. */
MPMarshalFormatNone, MPMarshalFormatNone,
@ -122,12 +125,12 @@ typedef struct MPMarshalledSite {
typedef struct MPMarshalledUser { typedef struct MPMarshalledUser {
MPMasterKeyProvider masterKeyProvider; MPMasterKeyProvider masterKeyProvider;
MPAlgorithmVersion algorithm;
bool redacted; bool redacted;
unsigned int avatar; unsigned int avatar;
const char *fullName; const char *fullName;
MPIdenticon identicon; MPIdenticon identicon;
MPAlgorithmVersion algorithm;
const char *keyID; const char *keyID;
MPResultType defaultType; MPResultType defaultType;
time_t lastUsed; time_t lastUsed;
@ -138,24 +141,24 @@ typedef struct MPMarshalledUser {
typedef struct MPMarshalledFile { typedef struct MPMarshalledFile {
MPMarshalledInfo *info; MPMarshalledInfo *info;
MPMarshalledUser *user;
MPMarshalledData *data; MPMarshalledData *data;
MPMarshalError error;
} MPMarshalledFile; } MPMarshalledFile;
//// Marshalling. //// Marshalling.
/** Write the user and all associated data out using the given marshalling format. /** Write the user and all associated data out using the given marshalling format.
* @return A string (allocated), or NULL if the format is unrecognized, does not support marshalling or a format error occurred. */ * @return A string (allocated), or NULL if the file is missing, format is unrecognized, does not support marshalling or a format error occurred. */
const char *mpw_marshal_write( const char *mpw_marshal_write(
const MPMarshalFormat outFormat, MPMarshalledFile *file, MPMarshalError *error); const MPMarshalFormat outFormat, MPMarshalledFile *file, MPMarshalledUser *user);
/** Best effort parse of metadata on the sites in the input buffer. Fields that could not be parsed remain at their type's initial value. /** Parse the user configuration in the input buffer. Fields that could not be parsed remain at their type's initial value.
* @return A metadata object (allocated); NULL if the object could not be allocated or the format was not understood. */ * @return The updated file object or a new one (allocated) if none was provided; NULL if a file object could not be allocated. */
MPMarshalledInfo *mpw_marshal_read_info(
const char *in);
/** Unmarshall sites in the given input buffer by parsing it using the given marshalling format.
* @return A user object (allocated), or NULL if the format provides no marshalling or a format error occurred. */
MPMarshalledFile *mpw_marshal_read( MPMarshalledFile *mpw_marshal_read(
const char *in, const MPMasterKeyProvider masterKeyProvider, MPMarshalError *error); MPMarshalledFile *file, const char *in);
/** Authenticate as the user identified by the given marshalled file.
* @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);
//// Creating. //// Creating.
@ -175,7 +178,7 @@ MPMarshalledQuestion *mpw_marshal_question(
/** Create or update a marshal file descriptor. /** Create or update a marshal file descriptor.
* @return The given file or new (allocated) if file is NULL; or NULL if the user is missing or the file couldn't be allocated. */ * @return The given file or new (allocated) if file is NULL; or NULL if the user is missing or the file couldn't be allocated. */
MPMarshalledFile *mpw_marshal_file( MPMarshalledFile *mpw_marshal_file(
MPMarshalledFile *const file, MPMarshalledUser *user, MPMarshalledData *data, MPMarshalledInfo *info); MPMarshalledFile *file, MPMarshalledInfo *info, MPMarshalledData *data);
//// Disposing. //// Disposing.
@ -241,7 +244,7 @@ bool mpw_marshal_data_set_num(
bool mpw_marshal_data_vset_num( bool mpw_marshal_data_vset_num(
const double value, MPMarshalledData *data, va_list nodes); const double value, MPMarshalledData *data, va_list nodes);
/** Look up the string value at the given path in the data store. /** Look up the string value at the given path in the data store.
* @return The string value (allocated) or string representation of the number at this path; NULL if there is no such value at this path. */ * @return The string value (shared) or string representation of the number at this path; NULL if there is no such value at this path. */
const char *mpw_marshal_data_get_str( const char *mpw_marshal_data_get_str(
const MPMarshalledData *data, ...); const MPMarshalledData *data, ...);
const char *mpw_marshal_data_vget_str( const char *mpw_marshal_data_vget_str(