Full file marshalling and prepare to make entire document available.
This commit is contained in:
		@@ -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 );
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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(
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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. */
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user