2
0

Full file marshalling and prepare to make entire document available.

This commit is contained in:
Maarten Billemont 2019-07-25 12:06:25 -04:00
parent 2fdd9d2ca1
commit 9833f02339
4 changed files with 208 additions and 102 deletions

View File

@ -156,7 +156,7 @@ typedef struct {
const char *purposeResult; const char *purposeResult;
const char *resultState; const char *resultState;
const char *resultParam; const char *resultParam;
MPMarshalledUser *user; MPMarshalledFile *file;
MPMarshalledSite *site; MPMarshalledSite *site;
MPMarshalledQuestion *question; MPMarshalledQuestion *question;
} Operation; } Operation;
@ -185,7 +185,7 @@ void cli_save(Arguments *args, Operation *operation);
MPMasterKeyProvider cli_masterKeyProvider_op(Operation *operation); MPMasterKeyProvider cli_masterKeyProvider_op(Operation *operation);
MPMasterKeyProvider cli_masterKeyProvider_str(const char *masterPassword); MPMasterKeyProvider cli_masterKeyProvider_str(const char *masterPassword);
void cli_masterKeyProvider_free(); void cli_masterKeyProvider_free(void);
/** ======================================================================== /** ========================================================================
* MAIN */ * MAIN */
@ -233,8 +233,8 @@ int main(const int argc, char *const argv[]) {
// Operation summary. // Operation summary.
dbg( "-----------------" ); dbg( "-----------------" );
if (operation.user) { if (operation.file && operation.file->user) {
dbg( "fullName : %s", operation.user->fullName ); dbg( "fullName : %s", operation.file->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 );
@ -272,7 +272,7 @@ void cli_free(Arguments *args, Operation *operation) {
mpw_free_strings( &operation->fullName, &operation->masterPassword, &operation->siteName, NULL ); mpw_free_strings( &operation->fullName, &operation->masterPassword, &operation->siteName, NULL );
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_free( &operation->user ); mpw_marshal_free( &operation->file );
operation->site = NULL; operation->site = NULL;
operation->question = NULL; operation->question = NULL;
cli_masterKeyProvider_free(); cli_masterKeyProvider_free();
@ -484,24 +484,25 @@ void cli_user(Arguments *args, Operation *operation) {
} }
mpw_free( &extensions, count * sizeof( *extensions ) ); mpw_free( &extensions, count * sizeof( *extensions ) );
// Load the user object from mpsites. if (!sitesFile) {
if (!sitesFile) // If no user from mpsites, create a new one.
mpw_free_string( &operation->sitesPath ); mpw_free_string( &operation->sitesPath );
mpw_marshal_free( &operation->file );
operation->file = mpw_marshal_file( mpw_marshal_user(
operation->fullName, cli_masterKeyProvider_op( operation ), MPAlgorithmVersionCurrent ) );
}
else { else {
// Read file. // Load the user object from mpsites.
const char *sitesInputData = mpw_read_file( sitesFile ); const char *sitesInputData = mpw_read_file( sitesFile );
if (!sitesInputData || ferror( sitesFile )) if (!sitesInputData || ferror( sitesFile ))
wrn( "Error while reading configuration file:\n %s: %d", operation->sitesPath, ferror( sitesFile ) ); wrn( "Error while reading configuration file:\n %s: %d", operation->sitesPath, ferror( sitesFile ) );
fclose( sitesFile ); fclose( sitesFile );
// Parse file. // Parse file.
MPMarshalInfo *sitesInputInfo = mpw_marshal_read_info( sitesInputData );
MPMarshalFormat sitesInputFormat = args->sitesFormat? operation->sitesFormat: sitesInputInfo->format;
MPMarshalError marshalError = { .type = MPMarshalSuccess }; MPMarshalError marshalError = { .type = MPMarshalSuccess };
mpw_marshal_info_free( &sitesInputInfo ); mpw_marshal_free( &operation->file );
mpw_marshal_free( &operation->user ); operation->file = mpw_marshal_read( sitesInputData,
operation->user = mpw_marshal_read( sitesInputData, sitesInputFormat,
cli_masterKeyProvider_op( operation ), &marshalError ); cli_masterKeyProvider_op( operation ), &marshalError );
if (marshalError.type == MPMarshalErrorMasterPassword && operation->allowPasswordUpdate) { if (marshalError.type == MPMarshalErrorMasterPassword && operation->allowPasswordUpdate) {
// Update master password in mpsites. // Update master password in mpsites.
@ -515,10 +516,11 @@ void cli_user(Arguments *args, Operation *operation) {
importMasterPassword = mpw_getpass( "Old master password: " ); importMasterPassword = mpw_getpass( "Old master password: " );
} }
mpw_marshal_free( &operation->user ); mpw_marshal_free( &operation->file );
operation->user = mpw_marshal_read( sitesInputData, sitesInputFormat, operation->file = mpw_marshal_read( sitesInputData,
cli_masterKeyProvider_str( importMasterPassword ), &marshalError ); cli_masterKeyProvider_str( importMasterPassword ), &marshalError );
operation->user->masterKeyProvider = cli_masterKeyProvider_op( operation ); if (operation->file && operation->file->user)
operation->file->user->masterKeyProvider = cli_masterKeyProvider_op( operation );
mpw_free_string( &importMasterPassword ); mpw_free_string( &importMasterPassword );
} }
} }
@ -532,18 +534,12 @@ void cli_user(Arguments *args, Operation *operation) {
} }
// Any other parse error. // Any other parse error.
if (!operation->user || marshalError.type != MPMarshalSuccess) { if (!operation->file || !operation->file->user || marshalError.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, marshalError.message );
cli_free( args, operation ); cli_free( args, operation );
exit( EX_DATAERR ); exit( EX_DATAERR );
} }
} }
// If no user from mpsites, create a new one.
if (!operation->user) {
operation->user = mpw_marshal_user(
operation->fullName, cli_masterKeyProvider_op( operation ), MPAlgorithmVersionCurrent );
}
} }
void cli_site(Arguments *args, Operation *operation) { void cli_site(Arguments *args, Operation *operation) {
@ -552,14 +548,15 @@ void cli_site(Arguments *args, Operation *operation) {
abort(); abort();
// Load the site object from mpsites. // Load the site object from mpsites.
for (size_t s = 0; !operation->site && s < operation->user->sites_count; ++s) MPMarshalledUser *user = operation->file->user;
if (strcmp( operation->siteName, (&operation->user->sites[s])->siteName ) == 0) for (size_t s = 0; !operation->site && s < user->sites_count; ++s)
operation->site = &operation->user->sites[s]; if (strcmp( operation->siteName, (&user->sites[s])->siteName ) == 0)
operation->site = &user->sites[s];
// If no site from mpsites, create a new one. // If no site from mpsites, create a new one.
if (!operation->site) if (!operation->site)
operation->site = mpw_marshal_site( operation->site = mpw_marshal_site(
operation->user, operation->siteName, operation->user->defaultType, MPCounterValueDefault, operation->user->algorithm ); user, operation->siteName, user->defaultType, MPCounterValueDefault, user->algorithm );
} }
void cli_question(Arguments *args, Operation *operation) { void cli_question(Arguments *args, Operation *operation) {
@ -588,8 +585,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->user->identicon = mpw_identicon( operation->user->fullName, operation->masterPassword ); operation->file->user->identicon = mpw_identicon( operation->file->user->fullName, operation->masterPassword );
operation->identicon = mpw_identicon_render( operation->user->identicon ); operation->identicon = mpw_identicon_render( operation->file->user->identicon );
if (!operation->site) if (!operation->site)
abort(); abort();
@ -703,9 +700,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->user->redacted = strcmp( args->sitesRedacted, "1" ) == 0; operation->file->user->redacted = strcmp( args->sitesRedacted, "1" ) == 0;
else if (!operation->user->redacted) else if (!operation->file->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 +713,21 @@ void cli_mpw(Arguments *args, Operation *operation) {
if (mpw_verbosity >= inf_level) if (mpw_verbosity >= inf_level)
fprintf( stderr, "%s's %s for %s:\n[ %s ]: ", fprintf( stderr, "%s's %s for %s:\n[ %s ]: ",
operation->user->fullName, operation->purposeResult, operation->site->siteName, operation->identicon ); operation->file->user->fullName, operation->purposeResult, operation->site->siteName, operation->identicon );
// Determine master key. // Determine master key.
MPMasterKey masterKey = NULL; MPMasterKey masterKey = NULL;
if (operation->user->masterKeyProvider) if (operation->file->user->masterKeyProvider)
masterKey = operation->user->masterKeyProvider( operation->site->algorithm, operation->user->fullName ); masterKey = operation->file->user->masterKeyProvider( operation->site->algorithm, operation->file->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->user->keyID) if (!operation->file->user->keyID)
operation->user->keyID = mpw_strdup( keyID ); operation->file->user->keyID = mpw_strdup( keyID );
else if (!mpw_id_buf_equals( keyID, operation->user->keyID )) { else if (!mpw_id_buf_equals( keyID, operation->file->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,7 +789,7 @@ 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->user->lastUsed = time( NULL ); operation->site->lastUsed = operation->file->user->lastUsed = time( NULL );
operation->site->uses++; operation->site->uses++;
} }
@ -807,7 +804,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->user->fullName, extensions[0] ); operation->sitesPath = mpw_path( operation->file->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 ) );
@ -818,7 +815,7 @@ void cli_save(Arguments *args, Operation *operation) {
} }
MPMarshalError marshalError = { .type = MPMarshalSuccess }; MPMarshalError marshalError = { .type = MPMarshalSuccess };
const char *buf = mpw_marshal_write( operation->sitesFormat, operation->user, &marshalError ); const char *buf = mpw_marshal_write( operation->sitesFormat, operation->file, &marshalError );
if (!buf || marshalError.type != MPMarshalSuccess) if (!buf || marshalError.type != MPMarshalSuccess)
wrn( "Couldn't encode updated configuration file:\n %s: %s", operation->sitesPath, marshalError.message ); wrn( "Couldn't encode updated configuration file:\n %s: %s", operation->sitesPath, marshalError.message );

View File

@ -98,6 +98,21 @@ MPMarshalledQuestion *mpw_marshal_question(
return question; return question;
} }
MPMarshalledFile *mpw_marshal_file(
MPMarshalledUser *user) {
MPMarshalledFile *file;
if (!user || !(file = malloc( sizeof( MPMarshalledFile ) )))
return NULL;
*file = (MPMarshalledFile){
.info = NULL,
.data = NULL,
.user = user,
};
return file;
}
bool mpw_marshal_info_free( bool mpw_marshal_info_free(
MPMarshalInfo **info) { MPMarshalInfo **info) {
@ -111,15 +126,13 @@ bool mpw_marshal_info_free(
return success; return success;
} }
bool mpw_marshal_free( static bool mpw_marshal_user_free(
MPMarshalledUser **user) { MPMarshalledUser **user) {
if (!user || !*user) if (!user || !*user)
return true; return true;
bool success = true; bool success = mpw_free_strings( &(*user)->fullName, &(*user)->keyID, NULL );
success &= mpw_free_strings( &(*user)->fullName, NULL );
success &= mpw_free_strings( &(*user)->keyID, NULL );
for (size_t s = 0; s < (*user)->sites_count; ++s) { for (size_t s = 0; s < (*user)->sites_count; ++s) {
MPMarshalledSite *site = &(*user)->sites[s]; MPMarshalledSite *site = &(*user)->sites[s];
@ -138,10 +151,45 @@ bool mpw_marshal_free(
return success; return success;
} }
static bool mpw_marshal_data_free(
MPMarshalledData **data) {
if (!data || !*data)
return true;
bool success = mpw_free_strings( &(*data)->key, &(*data)->str_value, NULL );
for (unsigned int c = 0; c < (*data)->obj_children_count; ++c)
success &= mpw_marshal_data_free( &(*data)->obj_children[c] );
success &= mpw_free( data, sizeof( MPMarshalledData ) );
return success;
}
bool mpw_marshal_free(
MPMarshalledFile **file) {
if (!file || !*file)
return true;
bool success = true;
success &= mpw_marshal_info_free( &(*file)->info );
success &= mpw_marshal_user_free( &(*file)->user );
success &= mpw_marshal_data_free( &(*file)->data );
success &= mpw_free( file, sizeof( MPMarshalledFile ) );
return success;
}
static const char *mpw_marshal_write_flat( static const char *mpw_marshal_write_flat(
const MPMarshalledUser *user, MPMarshalError *error) { const MPMarshalledFile *file, MPMarshalError *error) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
MPMarshalledUser *user = file->user;
if (!user) {
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing user." };
return NULL;
}
if (!user->fullName || !strlen( user->fullName )) { if (!user->fullName || !strlen( user->fullName )) {
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." }; *error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." };
return NULL; return NULL;
@ -225,9 +273,14 @@ static const char *mpw_marshal_write_flat(
#if MPW_JSON #if MPW_JSON
static const char *mpw_marshal_write_json( static const char *mpw_marshal_write_json(
const MPMarshalledUser *user, MPMarshalError *error) { const MPMarshalledFile *file, MPMarshalError *error) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
MPMarshalledUser *user = file->user;
if (!user) {
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing user." };
return NULL;
}
if (!user->fullName || !strlen( user->fullName )) { if (!user->fullName || !strlen( user->fullName )) {
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." }; *error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." };
return NULL; return NULL;
@ -361,17 +414,17 @@ static const char *mpw_marshal_write_json(
#endif #endif
const char *mpw_marshal_write( const char *mpw_marshal_write(
const MPMarshalFormat outFormat, const MPMarshalledUser *user, MPMarshalError *error) { const MPMarshalFormat outFormat, const MPMarshalledFile *file, MPMarshalError *error) {
switch (outFormat) { switch (outFormat) {
case MPMarshalFormatNone: case MPMarshalFormatNone:
*error = (MPMarshalError){ .type = MPMarshalSuccess }; *error = (MPMarshalError){ .type = MPMarshalSuccess };
return NULL; return NULL;
case MPMarshalFormatFlat: case MPMarshalFormatFlat:
return mpw_marshal_write_flat( user, error ); return mpw_marshal_write_flat( file, error );
#if MPW_JSON #if MPW_JSON
case MPMarshalFormatJSON: case MPMarshalFormatJSON:
return mpw_marshal_write_json( user, error ); return mpw_marshal_write_json( file, error );
#endif #endif
default: default:
*error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported output format: %u", outFormat ) }; *error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported output format: %u", outFormat ) };
@ -430,7 +483,7 @@ static void mpw_marshal_read_flat_info(
} }
} }
static MPMarshalledUser *mpw_marshal_read_flat( static MPMarshalledFile *mpw_marshal_read_flat(
const char *in, MPMasterKeyProvider masterKeyProvider, MPMarshalError *error) { const char *in, MPMasterKeyProvider masterKeyProvider, MPMarshalError *error) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
@ -474,23 +527,23 @@ static MPMarshalledUser *mpw_marshal_read_flat(
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
mpw_free_strings( &fullName, &keyID, NULL ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
if (keyID && masterKey && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) { if (keyID && masterKey && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) {
*error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Master key doesn't match key ID." }; *error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Master key doesn't match key ID." };
mpw_free_strings( &fullName, &keyID, NULL ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
if (!(user = mpw_marshal_user( fullName, masterKeyProvider, algorithm ))) { if (!(user = mpw_marshal_user( fullName, masterKeyProvider, algorithm ))) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new user." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new user." };
mpw_free_strings( &fullName, &keyID, NULL ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
@ -512,7 +565,7 @@ static MPMarshalledUser *mpw_marshal_read_flat(
mpw_free_strings( &headerName, &headerValue, NULL ); mpw_free_strings( &headerName, &headerValue, NULL );
mpw_free_strings( &fullName, &keyID, NULL ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
@ -529,7 +582,7 @@ static MPMarshalledUser *mpw_marshal_read_flat(
mpw_free_strings( &headerName, &headerValue, NULL ); mpw_free_strings( &headerName, &headerValue, NULL );
mpw_free_strings( &fullName, &keyID, NULL ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
algorithm = (MPAlgorithmVersion)value; algorithm = (MPAlgorithmVersion)value;
@ -549,7 +602,7 @@ static MPMarshalledUser *mpw_marshal_read_flat(
mpw_free_strings( &headerName, &headerValue, NULL ); mpw_free_strings( &headerName, &headerValue, NULL );
mpw_free_strings( &fullName, &keyID, NULL ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
defaultType = (MPResultType)value; defaultType = (MPResultType)value;
@ -564,7 +617,7 @@ static MPMarshalledUser *mpw_marshal_read_flat(
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing header: Full Name" }; *error = (MPMarshalError){ MPMarshalErrorMissing, "Missing header: Full Name" };
mpw_free_strings( &fullName, &keyID, NULL ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
if (positionInLine >= endOfLine) if (positionInLine >= endOfLine)
@ -608,7 +661,7 @@ static MPMarshalledUser *mpw_marshal_read_flat(
*error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unexpected import format: %u", format ) }; *error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unexpected import format: %u", format ) };
mpw_free_strings( &fullName, &keyID, NULL ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
} }
@ -621,7 +674,7 @@ static MPMarshalledUser *mpw_marshal_read_flat(
mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL ); mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL );
mpw_free_strings( &fullName, &keyID, NULL ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
long long int value = atoll( str_counter ); long long int value = atoll( str_counter );
@ -631,7 +684,7 @@ static MPMarshalledUser *mpw_marshal_read_flat(
mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL ); mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL );
mpw_free_strings( &fullName, &keyID, NULL ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
MPCounterValue siteCounter = (MPCounterValue)value; MPCounterValue siteCounter = (MPCounterValue)value;
@ -642,7 +695,7 @@ static MPMarshalledUser *mpw_marshal_read_flat(
mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL ); mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL );
mpw_free_strings( &fullName, &keyID, NULL ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value; MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value;
@ -653,7 +706,7 @@ static MPMarshalledUser *mpw_marshal_read_flat(
mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL ); mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL );
mpw_free_strings( &fullName, &keyID, NULL ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
@ -664,7 +717,7 @@ static MPMarshalledUser *mpw_marshal_read_flat(
mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL ); mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL );
mpw_free_strings( &fullName, &keyID, NULL ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
@ -679,7 +732,7 @@ static MPMarshalledUser *mpw_marshal_read_flat(
mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL ); mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL );
mpw_free_strings( &fullName, &keyID, NULL ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
@ -707,7 +760,7 @@ static MPMarshalledUser *mpw_marshal_read_flat(
mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL ); mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL );
mpw_free_strings( &fullName, &keyID, NULL ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
@ -717,8 +770,15 @@ static MPMarshalledUser *mpw_marshal_read_flat(
mpw_free_strings( &fullName, &keyID, NULL ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
MPMarshalledFile *file = mpw_marshal_file( user );
if (!file) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new marshal file." };
mpw_marshal_user_free( &user );
return NULL;
}
*error = (MPMarshalError){ .type = MPMarshalSuccess }; *error = (MPMarshalError){ .type = MPMarshalSuccess };
return user; return file;
} }
#if MPW_JSON #if MPW_JSON
@ -750,7 +810,7 @@ static void mpw_marshal_read_json_info(
json_object_put( json_file ); json_object_put( json_file );
} }
static MPMarshalledUser *mpw_marshal_read_json( static MPMarshalledFile *mpw_marshal_read_json(
const char *in, MPMasterKeyProvider masterKeyProvider, MPMarshalError *error) { const char *in, MPMasterKeyProvider masterKeyProvider, MPMarshalError *error) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
@ -827,7 +887,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
if (!(user = mpw_marshal_user( fullName, masterKeyProvider, algorithm ))) { if (!(user = mpw_marshal_user( fullName, masterKeyProvider, algorithm ))) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new user." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new user." };
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
json_object_put( json_file ); json_object_put( json_file );
return NULL; return NULL;
} }
@ -848,7 +908,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) { if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site algorithm version: %s: %d", siteName, value ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site algorithm version: %s: %d", siteName, value ) };
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
json_object_put( json_file ); json_object_put( json_file );
return NULL; return NULL;
} }
@ -857,7 +917,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
if (value < MPCounterValueFirst || value > MPCounterValueLast) { if (value < MPCounterValueFirst || value > MPCounterValueLast) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site counter: %s: %d", siteName, value ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site counter: %s: %d", siteName, value ) };
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
json_object_put( json_file ); json_object_put( json_file );
return NULL; return NULL;
} }
@ -866,7 +926,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
if (!mpw_type_short_name( siteType )) { if (!mpw_type_short_name( siteType )) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site type: %s: %u", siteName, siteType ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site type: %s: %u", siteName, siteType ) };
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
json_object_put( json_file ); json_object_put( json_file );
return NULL; return NULL;
} }
@ -875,7 +935,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
if (!mpw_type_short_name( siteLoginType )) { if (!mpw_type_short_name( siteLoginType )) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site login type: %s: %u", siteName, siteLoginType ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site login type: %s: %u", siteName, siteLoginType ) };
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
json_object_put( json_file ); json_object_put( json_file );
return NULL; return NULL;
} }
@ -886,7 +946,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
if (!siteLastUsed) { if (!siteLastUsed) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) };
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
json_object_put( json_file ); json_object_put( json_file );
return NULL; return NULL;
} }
@ -898,7 +958,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
if (!site) { if (!site) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new site." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new site." };
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
json_object_put( json_file ); json_object_put( json_file );
return NULL; return NULL;
} }
@ -913,7 +973,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
if (!masterKeyProvider || !(masterKey = masterKeyProvider( site->algorithm, user->fullName ))) { if (!masterKeyProvider || !(masterKey = masterKeyProvider( site->algorithm, user->fullName ))) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_free( &user ); mpw_marshal_user_free( &user );
json_object_put( json_file ); json_object_put( json_file );
return NULL; return NULL;
} }
@ -958,8 +1018,15 @@ static MPMarshalledUser *mpw_marshal_read_json(
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
json_object_put( json_file ); json_object_put( json_file );
MPMarshalledFile *file = mpw_marshal_file( user );
if (!file) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new marshal file." };
mpw_marshal_user_free( &user );
return NULL;
}
*error = (MPMarshalError){ .type = MPMarshalSuccess }; *error = (MPMarshalError){ .type = MPMarshalSuccess };
return user; return file;
} }
#endif #endif
@ -993,23 +1060,31 @@ MPMarshalInfo *mpw_marshal_read_info(
return info; return info;
} }
MPMarshalledUser *mpw_marshal_read( MPMarshalledFile *mpw_marshal_read(
const char *in, const MPMarshalFormat inFormat, MPMasterKeyProvider masterKeyProvider, MPMarshalError *error) { const char *in, MPMasterKeyProvider masterKeyProvider, MPMarshalError *error) {
switch (inFormat) { MPMarshalInfo *info = mpw_marshal_read_info( in );
MPMarshalledFile *file = NULL;
switch (info->format) {
case MPMarshalFormatNone: case MPMarshalFormatNone:
*error = (MPMarshalError){ .type = MPMarshalSuccess }; *error = (MPMarshalError){ .type = MPMarshalSuccess };
return NULL; break;
case MPMarshalFormatFlat: case MPMarshalFormatFlat:
return mpw_marshal_read_flat( in, masterKeyProvider, error ); file = mpw_marshal_read_flat( in, masterKeyProvider, error );
break;
#if MPW_JSON #if MPW_JSON
case MPMarshalFormatJSON: case MPMarshalFormatJSON:
return mpw_marshal_read_json( in, masterKeyProvider, error ); file = mpw_marshal_read_json( in, masterKeyProvider, error );
break;
#endif #endif
default: default:
*error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported input format: %u", inFormat ) }; *error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported input format: %u", info->format ) };
return NULL; break;
} }
if (file)
file->info = info;
return file;
} }
const MPMarshalFormat mpw_format_named( const MPMarshalFormat mpw_format_named(

View File

@ -68,6 +68,19 @@ typedef struct MPMarshalError {
const char *message; const char *message;
} MPMarshalError; } MPMarshalError;
typedef struct MPMarshalInfo {
MPMarshalFormat format;
time_t exportDate;
bool redacted;
MPAlgorithmVersion algorithm;
unsigned int avatar;
const char *fullName;
MPIdenticon identicon;
const char *keyID;
time_t lastUsed;
} MPMarshalInfo;
typedef struct MPMarshalledQuestion { typedef struct MPMarshalledQuestion {
const char *keyword; const char *keyword;
MPResultType type; MPResultType type;
@ -109,33 +122,44 @@ typedef struct MPMarshalledUser {
MPMarshalledSite *sites; MPMarshalledSite *sites;
} MPMarshalledUser; } MPMarshalledUser;
typedef struct MPMarshalInfo { typedef struct MPMarshalledData {
MPMarshalFormat format; // If data is held in a parent object.
time_t exportDate; const char *key;
bool redacted; // If data is held in a parent array.
unsigned int index;
MPAlgorithmVersion algorithm; // If data is a string.
unsigned int avatar; const char *str_value;
const char *fullName; // If data is a boolean.
MPIdenticon identicon; bool bool_value;
const char *keyID; // If data is a number.
time_t lastUsed; double num_value;
} MPMarshalInfo;
// If data is an object or array.
struct MPMarshalledData **obj_children;
size_t obj_children_count;
} MPMarshalledData;
typedef struct MPMarshalledFile {
MPMarshalInfo *info;
MPMarshalledUser *user;
MPMarshalledData *data;
} 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 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, const MPMarshalledUser *user, MPMarshalError *error); const MPMarshalFormat outFormat, const MPMarshalledFile *file, MPMarshalError *error);
/** 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. /** 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.
* @return A metadata object (allocated); NULL if the object could not be allocated or the format was not understood. */ * @return A metadata object (allocated); NULL if the object could not be allocated or the format was not understood. */
MPMarshalInfo *mpw_marshal_read_info( MPMarshalInfo *mpw_marshal_read_info(
const char *in); const char *in);
/** Unmarshall sites in the given input buffer by parsing it using the given marshalling format. /** 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. */ * @return A user object (allocated), or NULL if the format provides no marshalling or a format error occurred. */
MPMarshalledUser *mpw_marshal_read( MPMarshalledFile *mpw_marshal_read(
const char *in, const MPMarshalFormat inFormat, MPMasterKeyProvider masterKeyProvider, MPMarshalError *error); const char *in, MPMasterKeyProvider masterKeyProvider, MPMarshalError *error);
//// Utilities. //// Utilities.
@ -152,12 +176,16 @@ MPMarshalledSite *mpw_marshal_site(
* @return A question object (allocated), or NULL if the marshalled question couldn't be allocated. */ * @return A question object (allocated), or NULL if the marshalled question couldn't be allocated. */
MPMarshalledQuestion *mpw_marshal_question( MPMarshalledQuestion *mpw_marshal_question(
MPMarshalledSite *site, const char *keyword); MPMarshalledSite *site, const char *keyword);
/** Create a new file to marshal a user into.
* @return A file object (allocated), or NULL if the user is missing or the marshalled file couldn't be allocated. */
MPMarshalledFile *mpw_marshal_file(
MPMarshalledUser *user);
/** Free the given user object and all associated data. */ /** Free the given user object and all associated data. */
bool mpw_marshal_info_free( bool mpw_marshal_info_free(
MPMarshalInfo **info); MPMarshalInfo **info);
bool mpw_marshal_free( bool mpw_marshal_free(
MPMarshalledUser **user); MPMarshalledFile **user);
//// Format. //// Format.

View File

@ -36,6 +36,12 @@ MP_LIBS_END
#define mpw_enum(_type, _name) _type _name; enum #define mpw_enum(_type, _name) _type _name; enum
#endif #endif
#ifdef NS_OPTIONS
#define mpw_opts(_type, _name) NS_OPTIONS(_type, _name)
#else
#define mpw_opts(_type, _name) _type _name; enum
#endif
//// Types. //// Types.
extern const size_t MPMasterKeySize, MPSiteKeySize; /* bytes */ extern const size_t MPMasterKeySize, MPSiteKeySize; /* bytes */
@ -52,7 +58,7 @@ typedef mpw_enum( uint8_t, MPKeyPurpose ) {
}; };
// bit 4 - 9 // bit 4 - 9
typedef mpw_enum( uint16_t, MPResultTypeClass ) { typedef mpw_opts( uint16_t, MPResultTypeClass ) {
/** Use the site key to generate a password from a template. */ /** Use the site key to generate a password from a template. */
MPResultTypeClassTemplate = 1 << 4, MPResultTypeClassTemplate = 1 << 4,
/** Use the site key to encrypt and decrypt a stateful entity. */ /** Use the site key to encrypt and decrypt a stateful entity. */
@ -62,7 +68,7 @@ typedef mpw_enum( uint16_t, MPResultTypeClass ) {
}; };
// bit 10 - 15 // bit 10 - 15
typedef mpw_enum( uint16_t, MPSiteFeature ) { typedef mpw_opts( uint16_t, MPSiteFeature ) {
/** Export the key-protected content data. */ /** Export the key-protected content data. */
MPSiteFeatureExportContent = 1 << 10, MPSiteFeatureExportContent = 1 << 10,
/** Never export content. */ /** Never export content. */