diff --git a/platform-independent/c/cli/src/mpw-cli.c b/platform-independent/c/cli/src/mpw-cli.c index 00dbb635..486ca33d 100644 --- a/platform-independent/c/cli/src/mpw-cli.c +++ b/platform-independent/c/cli/src/mpw-cli.c @@ -156,7 +156,7 @@ typedef struct { const char *purposeResult; const char *resultState; const char *resultParam; - MPMarshalledUser *user; + MPMarshalledFile *file; MPMarshalledSite *site; MPMarshalledQuestion *question; } Operation; @@ -185,7 +185,7 @@ void cli_save(Arguments *args, Operation *operation); MPMasterKeyProvider cli_masterKeyProvider_op(Operation *operation); MPMasterKeyProvider cli_masterKeyProvider_str(const char *masterPassword); -void cli_masterKeyProvider_free(); +void cli_masterKeyProvider_free(void); /** ======================================================================== * MAIN */ @@ -233,8 +233,8 @@ int main(const int argc, char *const argv[]) { // Operation summary. dbg( "-----------------" ); - if (operation.user) { - dbg( "fullName : %s", operation.user->fullName ); + if (operation.file && operation.file->user) { + dbg( "fullName : %s", operation.file->user->fullName ); dbg( "identicon : %s", operation.identicon ); dbg( "sitesFormat : %s%s", mpw_format_name( operation.sitesFormat ), operation.sitesFormatFixed? " (fixed)": "" ); 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->keyContext, &operation->resultState, &operation->resultParam, NULL ); mpw_free_strings( &operation->identicon, &operation->sitesPath, NULL ); - mpw_marshal_free( &operation->user ); + mpw_marshal_free( &operation->file ); operation->site = NULL; operation->question = NULL; cli_masterKeyProvider_free(); @@ -484,24 +484,25 @@ void cli_user(Arguments *args, Operation *operation) { } 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_marshal_free( &operation->file ); + operation->file = mpw_marshal_file( mpw_marshal_user( + operation->fullName, cli_masterKeyProvider_op( operation ), MPAlgorithmVersionCurrent ) ); + } else { - // Read file. + // Load the user object from mpsites. const char *sitesInputData = mpw_read_file( sitesFile ); if (!sitesInputData || ferror( sitesFile )) wrn( "Error while reading configuration file:\n %s: %d", operation->sitesPath, ferror( sitesFile ) ); fclose( sitesFile ); // Parse file. - MPMarshalInfo *sitesInputInfo = mpw_marshal_read_info( sitesInputData ); - MPMarshalFormat sitesInputFormat = args->sitesFormat? operation->sitesFormat: sitesInputInfo->format; MPMarshalError marshalError = { .type = MPMarshalSuccess }; - mpw_marshal_info_free( &sitesInputInfo ); - mpw_marshal_free( &operation->user ); - operation->user = mpw_marshal_read( sitesInputData, sitesInputFormat, + mpw_marshal_free( &operation->file ); + operation->file = mpw_marshal_read( sitesInputData, cli_masterKeyProvider_op( operation ), &marshalError ); if (marshalError.type == MPMarshalErrorMasterPassword && operation->allowPasswordUpdate) { // Update master password in mpsites. @@ -515,10 +516,11 @@ void cli_user(Arguments *args, Operation *operation) { importMasterPassword = mpw_getpass( "Old master password: " ); } - mpw_marshal_free( &operation->user ); - operation->user = mpw_marshal_read( sitesInputData, sitesInputFormat, + mpw_marshal_free( &operation->file ); + operation->file = mpw_marshal_read( sitesInputData, 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 ); } } @@ -532,18 +534,12 @@ void cli_user(Arguments *args, Operation *operation) { } // 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 ); cli_free( args, operation ); 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) { @@ -552,14 +548,15 @@ void cli_site(Arguments *args, Operation *operation) { abort(); // Load the site object from mpsites. - for (size_t s = 0; !operation->site && s < operation->user->sites_count; ++s) - if (strcmp( operation->siteName, (&operation->user->sites[s])->siteName ) == 0) - operation->site = &operation->user->sites[s]; + MPMarshalledUser *user = operation->file->user; + for (size_t s = 0; !operation->site && s < user->sites_count; ++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 (!operation->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) { @@ -588,8 +585,8 @@ void cli_question(Arguments *args, Operation *operation) { void cli_operation(Arguments *args, Operation *operation) { mpw_free_string( &operation->identicon ); - operation->user->identicon = mpw_identicon( operation->user->fullName, operation->masterPassword ); - operation->identicon = mpw_identicon_render( operation->user->identicon ); + operation->file->user->identicon = mpw_identicon( operation->file->user->fullName, operation->masterPassword ); + operation->identicon = mpw_identicon_render( operation->file->user->identicon ); if (!operation->site) abort(); @@ -703,9 +700,9 @@ void cli_algorithmVersion(Arguments *args, Operation *operation) { void cli_sitesRedacted(Arguments *args, Operation *operation) { 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." ); } @@ -716,21 +713,21 @@ void cli_mpw(Arguments *args, Operation *operation) { if (mpw_verbosity >= inf_level) 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. MPMasterKey masterKey = NULL; - if (operation->user->masterKeyProvider) - masterKey = operation->user->masterKeyProvider( operation->site->algorithm, operation->user->fullName ); + if (operation->file->user->masterKeyProvider) + masterKey = operation->file->user->masterKeyProvider( operation->site->algorithm, operation->file->user->fullName ); if (!masterKey) { ftl( "Couldn't derive master key." ); cli_free( args, operation ); exit( EX_SOFTWARE ); } MPKeyID keyID = mpw_id_buf( masterKey, MPMasterKeySize ); - if (!operation->user->keyID) - operation->user->keyID = mpw_strdup( keyID ); - else if (!mpw_id_buf_equals( keyID, operation->user->keyID )) { + if (!operation->file->user->keyID) + operation->file->user->keyID = mpw_strdup( keyID ); + else if (!mpw_id_buf_equals( keyID, operation->file->user->keyID )) { ftl( "Master key mismatch." ); cli_free( args, operation ); exit( EX_SOFTWARE ); @@ -792,7 +789,7 @@ void cli_mpw(Arguments *args, Operation *operation) { mpw_free_string( &result ); // Update usage metadata. - operation->site->lastUsed = operation->user->lastUsed = time( NULL ); + operation->site->lastUsed = operation->file->user->lastUsed = time( NULL ); operation->site->uses++; } @@ -807,7 +804,7 @@ void cli_save(Arguments *args, Operation *operation) { return; 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 ) ); mpw_free( &extensions, count * sizeof( *extensions ) ); @@ -818,7 +815,7 @@ void cli_save(Arguments *args, Operation *operation) { } 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) wrn( "Couldn't encode updated configuration file:\n %s: %s", operation->sitesPath, marshalError.message ); diff --git a/platform-independent/c/core/src/mpw-marshal.c b/platform-independent/c/core/src/mpw-marshal.c index c829ad3f..b95fc3ab 100644 --- a/platform-independent/c/core/src/mpw-marshal.c +++ b/platform-independent/c/core/src/mpw-marshal.c @@ -98,6 +98,21 @@ MPMarshalledQuestion *mpw_marshal_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( MPMarshalInfo **info) { @@ -111,15 +126,13 @@ bool mpw_marshal_info_free( return success; } -bool mpw_marshal_free( +static bool mpw_marshal_user_free( MPMarshalledUser **user) { if (!user || !*user) return true; - bool success = true; - success &= mpw_free_strings( &(*user)->fullName, NULL ); - success &= mpw_free_strings( &(*user)->keyID, NULL ); + bool success = mpw_free_strings( &(*user)->fullName, &(*user)->keyID, NULL ); for (size_t s = 0; s < (*user)->sites_count; ++s) { MPMarshalledSite *site = &(*user)->sites[s]; @@ -138,10 +151,45 @@ bool mpw_marshal_free( 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( - const MPMarshalledUser *user, MPMarshalError *error) { + const MPMarshalledFile *file, MPMarshalError *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 )) { *error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." }; return NULL; @@ -225,9 +273,14 @@ static const char *mpw_marshal_write_flat( #if MPW_JSON static const char *mpw_marshal_write_json( - const MPMarshalledUser *user, MPMarshalError *error) { + const MPMarshalledFile *file, MPMarshalError *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 )) { *error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." }; return NULL; @@ -361,17 +414,17 @@ static const char *mpw_marshal_write_json( #endif const char *mpw_marshal_write( - const MPMarshalFormat outFormat, const MPMarshalledUser *user, MPMarshalError *error) { + const MPMarshalFormat outFormat, const MPMarshalledFile *file, MPMarshalError *error) { switch (outFormat) { case MPMarshalFormatNone: *error = (MPMarshalError){ .type = MPMarshalSuccess }; return NULL; case MPMarshalFormatFlat: - return mpw_marshal_write_flat( user, error ); + return mpw_marshal_write_flat( file, error ); #if MPW_JSON case MPMarshalFormatJSON: - return mpw_marshal_write_json( user, error ); + return mpw_marshal_write_json( file, error ); #endif default: *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) { *error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." }; @@ -474,23 +527,23 @@ static MPMarshalledUser *mpw_marshal_read_flat( *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." }; mpw_free_strings( &fullName, &keyID, NULL ); mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); return NULL; } if (keyID && masterKey && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) { *error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Master key doesn't match key ID." }; mpw_free_strings( &fullName, &keyID, NULL ); mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); return NULL; } - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); if (!(user = mpw_marshal_user( fullName, masterKeyProvider, algorithm ))) { *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new user." }; mpw_free_strings( &fullName, &keyID, NULL ); mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); return NULL; } @@ -512,7 +565,7 @@ static MPMarshalledUser *mpw_marshal_read_flat( mpw_free_strings( &headerName, &headerValue, NULL ); mpw_free_strings( &fullName, &keyID, NULL ); mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); return NULL; } @@ -529,7 +582,7 @@ static MPMarshalledUser *mpw_marshal_read_flat( mpw_free_strings( &headerName, &headerValue, NULL ); mpw_free_strings( &fullName, &keyID, NULL ); mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); return NULL; } algorithm = (MPAlgorithmVersion)value; @@ -549,7 +602,7 @@ static MPMarshalledUser *mpw_marshal_read_flat( mpw_free_strings( &headerName, &headerValue, NULL ); mpw_free_strings( &fullName, &keyID, NULL ); mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); return NULL; } defaultType = (MPResultType)value; @@ -564,7 +617,7 @@ static MPMarshalledUser *mpw_marshal_read_flat( *error = (MPMarshalError){ MPMarshalErrorMissing, "Missing header: Full Name" }; mpw_free_strings( &fullName, &keyID, NULL ); mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); return NULL; } if (positionInLine >= endOfLine) @@ -608,7 +661,7 @@ static MPMarshalledUser *mpw_marshal_read_flat( *error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unexpected import format: %u", format ) }; mpw_free_strings( &fullName, &keyID, NULL ); mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); return NULL; } } @@ -621,7 +674,7 @@ static MPMarshalledUser *mpw_marshal_read_flat( mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL ); mpw_free_strings( &fullName, &keyID, NULL ); mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); return NULL; } 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( &fullName, &keyID, NULL ); mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); return NULL; } MPCounterValue siteCounter = (MPCounterValue)value; @@ -642,7 +695,7 @@ static MPMarshalledUser *mpw_marshal_read_flat( mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL ); mpw_free_strings( &fullName, &keyID, NULL ); mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); return NULL; } MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value; @@ -653,7 +706,7 @@ static MPMarshalledUser *mpw_marshal_read_flat( mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL ); mpw_free_strings( &fullName, &keyID, NULL ); mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); return NULL; } @@ -664,7 +717,7 @@ static MPMarshalledUser *mpw_marshal_read_flat( mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL ); mpw_free_strings( &fullName, &keyID, NULL ); mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); return NULL; } @@ -679,7 +732,7 @@ static MPMarshalledUser *mpw_marshal_read_flat( mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL ); mpw_free_strings( &fullName, &keyID, NULL ); mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); return NULL; } @@ -707,7 +760,7 @@ static MPMarshalledUser *mpw_marshal_read_flat( mpw_free_strings( &siteLoginState, &siteName, &siteResultState, NULL ); mpw_free_strings( &fullName, &keyID, NULL ); mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); return NULL; } @@ -717,8 +770,15 @@ static MPMarshalledUser *mpw_marshal_read_flat( mpw_free_strings( &fullName, &keyID, NULL ); 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 }; - return user; + return file; } #if MPW_JSON @@ -750,7 +810,7 @@ static void mpw_marshal_read_json_info( json_object_put( json_file ); } -static MPMarshalledUser *mpw_marshal_read_json( +static MPMarshalledFile *mpw_marshal_read_json( const char *in, MPMasterKeyProvider masterKeyProvider, MPMarshalError *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 ))) { *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new user." }; mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); json_object_put( json_file ); return NULL; } @@ -848,7 +908,7 @@ static MPMarshalledUser *mpw_marshal_read_json( if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) { *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site algorithm version: %s: %d", siteName, value ) }; mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); json_object_put( json_file ); return NULL; } @@ -857,7 +917,7 @@ static MPMarshalledUser *mpw_marshal_read_json( if (value < MPCounterValueFirst || value > MPCounterValueLast) { *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site counter: %s: %d", siteName, value ) }; mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); json_object_put( json_file ); return NULL; } @@ -866,7 +926,7 @@ static MPMarshalledUser *mpw_marshal_read_json( if (!mpw_type_short_name( siteType )) { *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site type: %s: %u", siteName, siteType ) }; mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); json_object_put( json_file ); return NULL; } @@ -875,7 +935,7 @@ static MPMarshalledUser *mpw_marshal_read_json( if (!mpw_type_short_name( siteLoginType )) { *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site login type: %s: %u", siteName, siteLoginType ) }; mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); json_object_put( json_file ); return NULL; } @@ -886,7 +946,7 @@ static MPMarshalledUser *mpw_marshal_read_json( if (!siteLastUsed) { *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) }; mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); json_object_put( json_file ); return NULL; } @@ -898,7 +958,7 @@ static MPMarshalledUser *mpw_marshal_read_json( if (!site) { *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new site." }; mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); json_object_put( json_file ); return NULL; } @@ -913,7 +973,7 @@ static MPMarshalledUser *mpw_marshal_read_json( if (!masterKeyProvider || !(masterKey = masterKeyProvider( site->algorithm, user->fullName ))) { *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." }; mpw_free( &masterKey, MPMasterKeySize ); - mpw_marshal_free( &user ); + mpw_marshal_user_free( &user ); json_object_put( json_file ); return NULL; } @@ -958,8 +1018,15 @@ static MPMarshalledUser *mpw_marshal_read_json( mpw_free( &masterKey, MPMasterKeySize ); 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 }; - return user; + return file; } #endif @@ -993,23 +1060,31 @@ MPMarshalInfo *mpw_marshal_read_info( return info; } -MPMarshalledUser *mpw_marshal_read( - const char *in, const MPMarshalFormat inFormat, MPMasterKeyProvider masterKeyProvider, MPMarshalError *error) { +MPMarshalledFile *mpw_marshal_read( + const char *in, MPMasterKeyProvider masterKeyProvider, MPMarshalError *error) { - switch (inFormat) { + MPMarshalInfo *info = mpw_marshal_read_info( in ); + MPMarshalledFile *file = NULL; + switch (info->format) { case MPMarshalFormatNone: *error = (MPMarshalError){ .type = MPMarshalSuccess }; - return NULL; + break; case MPMarshalFormatFlat: - return mpw_marshal_read_flat( in, masterKeyProvider, error ); + file = mpw_marshal_read_flat( in, masterKeyProvider, error ); + break; #if MPW_JSON case MPMarshalFormatJSON: - return mpw_marshal_read_json( in, masterKeyProvider, error ); + file = mpw_marshal_read_json( in, masterKeyProvider, error ); + break; #endif default: - *error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported input format: %u", inFormat ) }; - return NULL; + *error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported input format: %u", info->format ) }; + break; } + if (file) + file->info = info; + + return file; } const MPMarshalFormat mpw_format_named( diff --git a/platform-independent/c/core/src/mpw-marshal.h b/platform-independent/c/core/src/mpw-marshal.h index 5fee9970..27d57438 100644 --- a/platform-independent/c/core/src/mpw-marshal.h +++ b/platform-independent/c/core/src/mpw-marshal.h @@ -68,6 +68,19 @@ typedef struct MPMarshalError { const char *message; } 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 { const char *keyword; MPResultType type; @@ -109,33 +122,44 @@ typedef struct MPMarshalledUser { MPMarshalledSite *sites; } MPMarshalledUser; -typedef struct MPMarshalInfo { - MPMarshalFormat format; - time_t exportDate; - bool redacted; +typedef struct MPMarshalledData { + // If data is held in a parent object. + const char *key; + // If data is held in a parent array. + unsigned int index; - MPAlgorithmVersion algorithm; - unsigned int avatar; - const char *fullName; - MPIdenticon identicon; - const char *keyID; - time_t lastUsed; -} MPMarshalInfo; + // If data is a string. + const char *str_value; + // If data is a boolean. + bool bool_value; + // If data is a number. + double num_value; + + // 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. /** 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. */ 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. * @return A metadata object (allocated); NULL if the object could not be allocated or the format was not understood. */ MPMarshalInfo *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. */ -MPMarshalledUser *mpw_marshal_read( - const char *in, const MPMarshalFormat inFormat, MPMasterKeyProvider masterKeyProvider, MPMarshalError *error); +MPMarshalledFile *mpw_marshal_read( + const char *in, MPMasterKeyProvider masterKeyProvider, MPMarshalError *error); //// Utilities. @@ -152,12 +176,16 @@ MPMarshalledSite *mpw_marshal_site( * @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 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. */ bool mpw_marshal_info_free( MPMarshalInfo **info); bool mpw_marshal_free( - MPMarshalledUser **user); + MPMarshalledFile **user); //// Format. diff --git a/platform-independent/c/core/src/mpw-types.h b/platform-independent/c/core/src/mpw-types.h index f4e25020..b69588f7 100644 --- a/platform-independent/c/core/src/mpw-types.h +++ b/platform-independent/c/core/src/mpw-types.h @@ -36,6 +36,12 @@ MP_LIBS_END #define mpw_enum(_type, _name) _type _name; enum #endif +#ifdef NS_OPTIONS +#define mpw_opts(_type, _name) NS_OPTIONS(_type, _name) +#else +#define mpw_opts(_type, _name) _type _name; enum +#endif + //// Types. extern const size_t MPMasterKeySize, MPSiteKeySize; /* bytes */ @@ -52,7 +58,7 @@ typedef mpw_enum( uint8_t, MPKeyPurpose ) { }; // 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. */ MPResultTypeClassTemplate = 1 << 4, /** Use the site key to encrypt and decrypt a stateful entity. */ @@ -62,7 +68,7 @@ typedef mpw_enum( uint16_t, MPResultTypeClass ) { }; // bit 10 - 15 -typedef mpw_enum( uint16_t, MPSiteFeature ) { +typedef mpw_opts( uint16_t, MPSiteFeature ) { /** Export the key-protected content data. */ MPSiteFeatureExportContent = 1 << 10, /** Never export content. */