Refactor in preparation of reading ext data prior to auth.
This commit is contained in:
		@@ -565,7 +565,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
           saveInContext:(NSManagedObjectContext *)context {
 | 
			
		||||
 | 
			
		||||
    // Read metadata for the import file.
 | 
			
		||||
    MPMarshalInfo *info = mpw_marshal_read_info( importData.UTF8String );
 | 
			
		||||
    MPMarshalledInfo *info = mpw_marshal_read_info( importData.UTF8String );
 | 
			
		||||
    if (info->format == MPMarshalFormatNone)
 | 
			
		||||
        return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
 | 
			
		||||
                @"type"                  : @(MPMarshalErrorFormat),
 | 
			
		||||
 
 | 
			
		||||
@@ -489,7 +489,7 @@ void cli_user(Arguments *args, Operation *operation) {
 | 
			
		||||
        mpw_free_string( &operation->sitesPath );
 | 
			
		||||
        mpw_marshal_file_free( &operation->file );
 | 
			
		||||
        operation->file = mpw_marshal_file( NULL, mpw_marshal_user(
 | 
			
		||||
                operation->fullName, cli_masterKeyProvider_op( operation ), MPAlgorithmVersionCurrent ), NULL, NULL );
 | 
			
		||||
                operation->fullName, cli_masterKeyProvider_op( operation ), MPAlgorithmVersionCurrent ), NULL );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    else {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@
 | 
			
		||||
 | 
			
		||||
MP_LIBS_BEGIN
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
MP_LIBS_END
 | 
			
		||||
 | 
			
		||||
char *mpw_get_token(const char **in, const char *eol, const char *delim) {
 | 
			
		||||
@@ -57,6 +58,22 @@ time_t mpw_timegm(const char *time) {
 | 
			
		||||
    return ERR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mpw_update_master_key(MPMasterKey *masterKey, MPAlgorithmVersion *masterKeyAlgorithm, const MPAlgorithmVersion targetKeyAlgorithm,
 | 
			
		||||
        const char *fullName, const char *masterPassword) {
 | 
			
		||||
 | 
			
		||||
    if (masterKey && (!*masterKey || *masterKeyAlgorithm != targetKeyAlgorithm)) {
 | 
			
		||||
        mpw_free( masterKey, MPMasterKeySize );
 | 
			
		||||
        *masterKeyAlgorithm = targetKeyAlgorithm;
 | 
			
		||||
        *masterKey = mpw_master_key( fullName, masterPassword, *masterKeyAlgorithm );
 | 
			
		||||
        if (!*masterKey) {
 | 
			
		||||
            err( "Couldn't derive master key for user %s, algorithm %d.", fullName, *masterKeyAlgorithm );
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if MPW_JSON
 | 
			
		||||
 | 
			
		||||
json_object *mpw_get_json_object(
 | 
			
		||||
@@ -105,20 +122,110 @@ bool mpw_get_json_boolean(
 | 
			
		||||
    return json_object_get_boolean( json_value ) == true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
void mpw_set_json_data(
 | 
			
		||||
        MPMarshalledData *data, json_object *obj) {
 | 
			
		||||
 | 
			
		||||
bool mpw_update_master_key(MPMasterKey *masterKey, MPAlgorithmVersion *masterKeyAlgorithm, const MPAlgorithmVersion targetKeyAlgorithm,
 | 
			
		||||
        const char *fullName, const char *masterPassword) {
 | 
			
		||||
    if (!data)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (masterKey && (!*masterKey || *masterKeyAlgorithm != targetKeyAlgorithm)) {
 | 
			
		||||
        mpw_free( masterKey, MPMasterKeySize );
 | 
			
		||||
        *masterKeyAlgorithm = targetKeyAlgorithm;
 | 
			
		||||
        *masterKey = mpw_master_key( fullName, masterPassword, *masterKeyAlgorithm );
 | 
			
		||||
        if (!*masterKey) {
 | 
			
		||||
            err( "Couldn't derive master key for user %s, algorithm %d.", fullName, *masterKeyAlgorithm );
 | 
			
		||||
            return false;
 | 
			
		||||
    json_type type = json_object_get_type( obj );
 | 
			
		||||
    data->is_null = type == json_type_null;
 | 
			
		||||
    data->is_bool = type == json_type_boolean;
 | 
			
		||||
 | 
			
		||||
    if (type == json_type_boolean)
 | 
			
		||||
        data->num_value = json_object_get_boolean( obj );
 | 
			
		||||
    else if (type == json_type_double)
 | 
			
		||||
        data->num_value = json_object_get_double( obj );
 | 
			
		||||
    else if (type == json_type_int)
 | 
			
		||||
        data->num_value = json_object_get_int64( obj );
 | 
			
		||||
    else
 | 
			
		||||
        data->num_value = NAN;
 | 
			
		||||
 | 
			
		||||
    const char *str = NULL;
 | 
			
		||||
    if (type == json_type_string || !isnan( data->num_value ))
 | 
			
		||||
        str = json_object_get_string( obj );
 | 
			
		||||
    if (!str || !data->str_value || strcmp( str, data->str_value ) != OK) {
 | 
			
		||||
        mpw_free_string( &data->str_value );
 | 
			
		||||
        data->str_value = mpw_strdup( str );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Clean up children
 | 
			
		||||
    MPMarshalledData *newChildren = NULL;
 | 
			
		||||
    size_t newChildrenCount = 0;
 | 
			
		||||
    for (size_t c = 0; c < data->children_count; ++c) {
 | 
			
		||||
        MPMarshalledData *child = &data->children[c];
 | 
			
		||||
        if ((type != json_type_object && type != json_type_array) || (child->obj_key && type != json_type_object)) {
 | 
			
		||||
            // Not a valid child in this object, remove it.
 | 
			
		||||
            mpw_marshal_data_set_null( child, NULL );
 | 
			
		||||
            mpw_free_string( &child->obj_key );
 | 
			
		||||
            if (!newChildren)
 | 
			
		||||
                newChildren = mpw_memdup( data->children, sizeof( MPMarshalledData ) * newChildrenCount );
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            // Valid child in this object, keep it.
 | 
			
		||||
            ++newChildrenCount;
 | 
			
		||||
            if (newChildren) {
 | 
			
		||||
                if (!mpw_realloc( &newChildren, NULL, sizeof( MPMarshalledData ) * newChildrenCount )) {
 | 
			
		||||
                    --newChildrenCount;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                child->arr_index = newChildrenCount - 1;
 | 
			
		||||
                newChildren[child->arr_index] = *child;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (newChildren) {
 | 
			
		||||
        mpw_free( &data->children, sizeof( MPMarshalledData ) * data->children_count );
 | 
			
		||||
        data->children = newChildren;
 | 
			
		||||
        data->children_count = newChildrenCount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Object
 | 
			
		||||
    if (type == json_type_object) {
 | 
			
		||||
        json_object_iter entry;
 | 
			
		||||
        json_object_object_foreachC( obj, entry ) {
 | 
			
		||||
            MPMarshalledData *child = NULL;
 | 
			
		||||
 | 
			
		||||
            // Find existing child.
 | 
			
		||||
            for (size_t c = 0; c < data->children_count; ++c)
 | 
			
		||||
                if (data->children[c].obj_key == entry.key ||
 | 
			
		||||
                    (data->children[c].obj_key && entry.key && strcmp( data->children[c].obj_key, entry.key ) == OK)) {
 | 
			
		||||
                    child = &data->children[c];
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            // Create new child.
 | 
			
		||||
            if (!child) {
 | 
			
		||||
                if (!mpw_realloc( &data->children, NULL, sizeof( MPMarshalledData ) * ++data->children_count )) {
 | 
			
		||||
                    --data->children_count;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                *(child = &data->children[data->children_count - 1]) = (MPMarshalledData){ .obj_key = mpw_strdup( entry.key ) };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            mpw_set_json_data( child, entry.val );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
    // Array
 | 
			
		||||
    if (type == json_type_array) {
 | 
			
		||||
        for (size_t index = 0; index < json_object_array_length( obj ); ++index) {
 | 
			
		||||
            MPMarshalledData *child = NULL;
 | 
			
		||||
 | 
			
		||||
            if (index < data->children_count)
 | 
			
		||||
                child = &data->children[index];
 | 
			
		||||
 | 
			
		||||
            else {
 | 
			
		||||
                if (!mpw_realloc( &data->children, NULL, sizeof( MPMarshalledData ) * ++data->children_count )) {
 | 
			
		||||
                    --data->children_count;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                *(child = &data->children[data->children_count - 1]) = (MPMarshalledData){ .arr_index = index };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            mpw_set_json_data( child, json_object_array_get_idx( obj, index ) );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@
 | 
			
		||||
#define _MPW_MARSHAL_UTIL_H
 | 
			
		||||
 | 
			
		||||
#include "mpw-algorithm.h"
 | 
			
		||||
#include "mpw-marshal.h"
 | 
			
		||||
 | 
			
		||||
MP_LIBS_BEGIN
 | 
			
		||||
#include <time.h>
 | 
			
		||||
@@ -40,6 +41,17 @@ char *mpw_get_token(
 | 
			
		||||
time_t mpw_timegm(
 | 
			
		||||
        const char *time);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// mpw.
 | 
			
		||||
 | 
			
		||||
/** Calculate a master key if the target master key algorithm is different from the given master key algorithm.
 | 
			
		||||
 * @param masterKey A buffer (allocated, MPMasterKeySize).
 | 
			
		||||
 * @return false if an error occurred during the derivation of the master key. */
 | 
			
		||||
bool mpw_update_master_key(
 | 
			
		||||
        MPMasterKey *masterKey, MPAlgorithmVersion *masterKeyAlgorithm, const MPAlgorithmVersion targetKeyAlgorithm,
 | 
			
		||||
        const char *fullName, const char *masterPassword);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// JSON parsing.
 | 
			
		||||
 | 
			
		||||
#if MPW_JSON
 | 
			
		||||
@@ -64,15 +76,11 @@ int64_t mpw_get_json_int(
 | 
			
		||||
 * @return The boolean value or defaultValue if one of the path's object keys was not found in the source object's tree. */
 | 
			
		||||
bool mpw_get_json_boolean(
 | 
			
		||||
        json_object *obj, const char *key, const bool defaultValue);
 | 
			
		||||
/** Translate a JSON object tree into a source-agnostic data object.
 | 
			
		||||
 * @param data A Master Password data object or NULL.
 | 
			
		||||
 * @param obj A JSON object tree or NULL. */
 | 
			
		||||
void mpw_set_json_data(
 | 
			
		||||
        MPMarshalledData *data, json_object *obj);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/// mpw.
 | 
			
		||||
 | 
			
		||||
/** Calculate a master key if the target master key algorithm is different from the given master key algorithm.
 | 
			
		||||
 * @param masterKey A buffer (allocated, MPMasterKeySize).
 | 
			
		||||
 * @return false if an error occurred during the derivation of the master key. */
 | 
			
		||||
bool mpw_update_master_key(
 | 
			
		||||
        MPMasterKey *masterKey, MPAlgorithmVersion *masterKeyAlgorithm, const MPAlgorithmVersion targetKeyAlgorithm,
 | 
			
		||||
        const char *fullName, const char *masterPassword);
 | 
			
		||||
 | 
			
		||||
#endif // _MPW_MARSHAL_UTIL_H
 | 
			
		||||
 
 | 
			
		||||
@@ -106,7 +106,7 @@ MPMarshalledQuestion *mpw_marshal_question(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MPMarshalledFile *mpw_marshal_file(
 | 
			
		||||
        MPMarshalledFile *file, MPMarshalledUser *user, MPMarshalledData *data, MPMarshalInfo *info) {
 | 
			
		||||
        MPMarshalledFile *file, MPMarshalledUser *user, MPMarshalledData *data, MPMarshalledInfo *info) {
 | 
			
		||||
 | 
			
		||||
    if (!file) {
 | 
			
		||||
        if (!(file = malloc( sizeof( MPMarshalledFile ) )))
 | 
			
		||||
@@ -132,13 +132,13 @@ MPMarshalledFile *mpw_marshal_file(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mpw_marshal_info_free(
 | 
			
		||||
        MPMarshalInfo **info) {
 | 
			
		||||
        MPMarshalledInfo **info) {
 | 
			
		||||
 | 
			
		||||
    if (!info || !*info)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    mpw_free_strings( &(*info)->fullName, &(*info)->keyID, NULL );
 | 
			
		||||
    mpw_free( info, sizeof( MPMarshalInfo ) );
 | 
			
		||||
    mpw_free( info, sizeof( MPMarshalledInfo ) );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mpw_marshal_user_free(
 | 
			
		||||
@@ -171,7 +171,7 @@ void mpw_marshal_data_free(
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    mpw_marshal_data_set_null( *data, NULL );
 | 
			
		||||
    mpw_free_string( &(*data)->key );
 | 
			
		||||
    mpw_free_string( &(*data)->obj_key );
 | 
			
		||||
    mpw_free( data, sizeof( MPMarshalledData ) );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -204,7 +204,7 @@ MPMarshalledData *mpw_marshal_data_vget(
 | 
			
		||||
        child = NULL;
 | 
			
		||||
 | 
			
		||||
        for (size_t c = 0; c < parent->children_count; ++c) {
 | 
			
		||||
            const char *key = parent->children[c].key;
 | 
			
		||||
            const char *key = parent->children[c].obj_key;
 | 
			
		||||
            if (key && strcmp( node, key ) == OK) {
 | 
			
		||||
                child = &parent->children[c];
 | 
			
		||||
                break;
 | 
			
		||||
@@ -216,7 +216,7 @@ MPMarshalledData *mpw_marshal_data_vget(
 | 
			
		||||
                --parent->children_count;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            *(child = &parent->children[parent->children_count - 1]) = (MPMarshalledData){ .key = mpw_strdup( node ) };
 | 
			
		||||
            *(child = &parent->children[parent->children_count - 1]) = (MPMarshalledData){ .obj_key = mpw_strdup( node ) };
 | 
			
		||||
            mpw_marshal_data_set_null( child, NULL );
 | 
			
		||||
            child->is_null = false;
 | 
			
		||||
        }
 | 
			
		||||
@@ -244,7 +244,7 @@ const MPMarshalledData *mpw_marshal_data_vfind(
 | 
			
		||||
        child = NULL;
 | 
			
		||||
 | 
			
		||||
        for (size_t c = 0; c < parent->children_count; ++c) {
 | 
			
		||||
            const char *key = parent->children[c].key;
 | 
			
		||||
            const char *key = parent->children[c].obj_key;
 | 
			
		||||
            if (key && strcmp( node, key ) == OK) {
 | 
			
		||||
                child = &parent->children[c];
 | 
			
		||||
                break;
 | 
			
		||||
@@ -297,7 +297,7 @@ bool mpw_marshal_data_vset_null(
 | 
			
		||||
    mpw_free_string( &child->str_value );
 | 
			
		||||
    for (unsigned int c = 0; c < child->children_count; ++c) {
 | 
			
		||||
        mpw_marshal_data_set_null( &child->children[c], NULL );
 | 
			
		||||
        mpw_free_string( &child->children[c].key );
 | 
			
		||||
        mpw_free_string( &child->children[c].obj_key );
 | 
			
		||||
    }
 | 
			
		||||
    mpw_free( &child->children, sizeof( MPMarshalledData ) * child->children_count );
 | 
			
		||||
    child->children_count = 0;
 | 
			
		||||
@@ -447,7 +447,7 @@ bool mpw_marshal_data_set_str(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *mpw_marshal_write_flat(
 | 
			
		||||
        const MPMarshalledFile *file, MPMarshalError *error) {
 | 
			
		||||
        MPMarshalledFile *file, MPMarshalError *error) {
 | 
			
		||||
 | 
			
		||||
    *error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
 | 
			
		||||
    MPMarshalledUser *user = file->user;
 | 
			
		||||
@@ -557,14 +557,14 @@ static json_object *mpw_get_json_data(
 | 
			
		||||
    for (size_t c = 0; c < data->children_count; ++c) {
 | 
			
		||||
        MPMarshalledData *child = &data->children[c];
 | 
			
		||||
        if (!obj) {
 | 
			
		||||
            if (child->key)
 | 
			
		||||
            if (child->obj_key)
 | 
			
		||||
                obj = json_object_new_object();
 | 
			
		||||
            else
 | 
			
		||||
                obj = json_object_new_array();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (child->key)
 | 
			
		||||
            json_object_object_add( obj, child->key, mpw_get_json_data( child ) );
 | 
			
		||||
        if (child->obj_key)
 | 
			
		||||
            json_object_object_add( obj, child->obj_key, mpw_get_json_data( child ) );
 | 
			
		||||
        else
 | 
			
		||||
            json_object_array_add( obj, mpw_get_json_data( child ) );
 | 
			
		||||
    }
 | 
			
		||||
@@ -573,7 +573,7 @@ static json_object *mpw_get_json_data(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *mpw_marshal_write_json(
 | 
			
		||||
        const MPMarshalledFile *file, MPMarshalError *error) {
 | 
			
		||||
        MPMarshalledFile *file, MPMarshalError *error) {
 | 
			
		||||
 | 
			
		||||
    *error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
 | 
			
		||||
    MPMarshalledUser *user = file->user;
 | 
			
		||||
@@ -694,6 +694,9 @@ static const char *mpw_marshal_write_json(
 | 
			
		||||
        mpw_free_strings( &resultState, &loginState, NULL );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!file->data)
 | 
			
		||||
        file->data = mpw_marshal_data_new();
 | 
			
		||||
    mpw_set_json_data( file->data, json_file );
 | 
			
		||||
    const char *out = mpw_strdup( json_object_to_json_string_ext( json_file,
 | 
			
		||||
            JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_NOSLASHESCAPE ) );
 | 
			
		||||
    json_object_put( json_file );
 | 
			
		||||
@@ -716,7 +719,7 @@ const char *mpw_marshal_write(
 | 
			
		||||
        *error = (MPMarshalError){ .type = MPMarshalErrorMissing, "No file to marshal." };
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    if (file->data && file->data->key) {
 | 
			
		||||
    if (file->data && file->data->obj_key) {
 | 
			
		||||
        *error = (MPMarshalError){ .type = MPMarshalErrorInternal, "Illegal file data." };
 | 
			
		||||
        ftl( "Unexpected non-root file data." );
 | 
			
		||||
        return NULL;
 | 
			
		||||
@@ -745,7 +748,7 @@ const char *mpw_marshal_write(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mpw_marshal_read_flat_info(
 | 
			
		||||
        const char *in, MPMarshalInfo *info) {
 | 
			
		||||
        const char *in, MPMarshalledInfo *info) {
 | 
			
		||||
 | 
			
		||||
    info->algorithm = MPAlgorithmVersionCurrent;
 | 
			
		||||
 | 
			
		||||
@@ -1082,6 +1085,7 @@ static MPMarshalledFile *mpw_marshal_read_flat(
 | 
			
		||||
    mpw_free_strings( &fullName, &keyID, NULL );
 | 
			
		||||
    mpw_free( &masterKey, MPMasterKeySize );
 | 
			
		||||
 | 
			
		||||
    // TODO: serialize data structure for this file.
 | 
			
		||||
    MPMarshalledFile *file = mpw_marshal_file( NULL, user, NULL, NULL );
 | 
			
		||||
    if (!file) {
 | 
			
		||||
        *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new marshal file." };
 | 
			
		||||
@@ -1095,114 +1099,8 @@ static MPMarshalledFile *mpw_marshal_read_flat(
 | 
			
		||||
 | 
			
		||||
#if MPW_JSON
 | 
			
		||||
 | 
			
		||||
static void mpw_set_json_data(
 | 
			
		||||
        MPMarshalledData *data, json_object *obj) {
 | 
			
		||||
 | 
			
		||||
    if (!data)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    json_type type = json_object_get_type( obj );
 | 
			
		||||
    data->is_null = type == json_type_null;
 | 
			
		||||
    data->is_bool = type == json_type_boolean;
 | 
			
		||||
 | 
			
		||||
    if (type == json_type_boolean)
 | 
			
		||||
        data->num_value = json_object_get_boolean( obj );
 | 
			
		||||
    else if (type == json_type_double)
 | 
			
		||||
        data->num_value = json_object_get_double( obj );
 | 
			
		||||
    else if (type == json_type_int)
 | 
			
		||||
        data->num_value = json_object_get_int64( obj );
 | 
			
		||||
    else
 | 
			
		||||
        data->num_value = NAN;
 | 
			
		||||
 | 
			
		||||
    const char *str = NULL;
 | 
			
		||||
    if (type == json_type_string || !isnan( data->num_value ))
 | 
			
		||||
        str = json_object_get_string( obj );
 | 
			
		||||
    if (!str || !data->str_value || strcmp( str, data->str_value ) != OK) {
 | 
			
		||||
        mpw_free_string( &data->str_value );
 | 
			
		||||
        data->str_value = mpw_strdup( str );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Clean up children
 | 
			
		||||
    MPMarshalledData *newChildren = NULL;
 | 
			
		||||
    size_t newChildrenCount = 0;
 | 
			
		||||
    for (size_t c = 0; c < data->children_count; ++c) {
 | 
			
		||||
        MPMarshalledData *child = &data->children[c];
 | 
			
		||||
        if ((type != json_type_object && type != json_type_array) || (child->key && type != json_type_object)) {
 | 
			
		||||
            // Not a valid child in this object, remove it.
 | 
			
		||||
            mpw_marshal_data_set_null( child, NULL );
 | 
			
		||||
            mpw_free_string( &child->key );
 | 
			
		||||
            if (!newChildren)
 | 
			
		||||
                newChildren = mpw_memdup( data->children, sizeof( MPMarshalledData ) * newChildrenCount );
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            // Valid child in this object, keep it.
 | 
			
		||||
            ++newChildrenCount;
 | 
			
		||||
            if (newChildren) {
 | 
			
		||||
                if (!mpw_realloc( &newChildren, NULL, sizeof( MPMarshalledData ) * newChildrenCount )) {
 | 
			
		||||
                    --newChildrenCount;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                child->index = newChildrenCount - 1;
 | 
			
		||||
                newChildren[child->index] = *child;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (newChildren) {
 | 
			
		||||
        mpw_free( &data->children, sizeof( MPMarshalledData ) * data->children_count );
 | 
			
		||||
        data->children = newChildren;
 | 
			
		||||
        data->children_count = newChildrenCount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Object
 | 
			
		||||
    if (type == json_type_object) {
 | 
			
		||||
        json_object_iter entry;
 | 
			
		||||
        json_object_object_foreachC( obj, entry ) {
 | 
			
		||||
            MPMarshalledData *child = NULL;
 | 
			
		||||
 | 
			
		||||
            // Find existing child.
 | 
			
		||||
            for (size_t c = 0; c < data->children_count; ++c)
 | 
			
		||||
                if (data->children[c].key == entry.key ||
 | 
			
		||||
                    (data->children[c].key && entry.key && strcmp( data->children[c].key, entry.key ) == OK)) {
 | 
			
		||||
                    child = &data->children[c];
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            // Create new child.
 | 
			
		||||
            if (!child) {
 | 
			
		||||
                if (!mpw_realloc( &data->children, NULL, sizeof( MPMarshalledData ) * ++data->children_count )) {
 | 
			
		||||
                    --data->children_count;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                *(child = &data->children[data->children_count - 1]) = (MPMarshalledData){ .key = mpw_strdup( entry.key ) };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            mpw_set_json_data( child, entry.val );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Array
 | 
			
		||||
    if (type == json_type_array) {
 | 
			
		||||
        for (size_t index = 0; index < json_object_array_length( obj ); ++index) {
 | 
			
		||||
            MPMarshalledData *child = NULL;
 | 
			
		||||
 | 
			
		||||
            if (index < data->children_count)
 | 
			
		||||
                child = &data->children[index];
 | 
			
		||||
 | 
			
		||||
            else {
 | 
			
		||||
                if (!mpw_realloc( &data->children, NULL, sizeof( MPMarshalledData ) * ++data->children_count )) {
 | 
			
		||||
                    --data->children_count;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                *(child = &data->children[data->children_count - 1]) = (MPMarshalledData){ .index = index };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            mpw_set_json_data( child, json_object_array_get_idx( obj, index ) );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mpw_marshal_read_json_info(
 | 
			
		||||
        const char *in, MPMarshalInfo *info) {
 | 
			
		||||
        const char *in, MPMarshalledInfo *info) {
 | 
			
		||||
 | 
			
		||||
    // Parse JSON.
 | 
			
		||||
    enum json_tokener_error json_error = json_tokener_success;
 | 
			
		||||
@@ -1457,14 +1355,14 @@ static MPMarshalledFile *mpw_marshal_read_json(
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
MPMarshalInfo *mpw_marshal_read_info(
 | 
			
		||||
MPMarshalledInfo *mpw_marshal_read_info(
 | 
			
		||||
        const char *in) {
 | 
			
		||||
 | 
			
		||||
    MPMarshalInfo *info = malloc( sizeof( MPMarshalInfo ) );
 | 
			
		||||
    MPMarshalledInfo *info = malloc( sizeof( MPMarshalledInfo ) );
 | 
			
		||||
    if (!info)
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
    *info = (MPMarshalInfo){ .format = MPMarshalFormatNone, .identicon = MPIdenticonUnset };
 | 
			
		||||
    *info = (MPMarshalledInfo){ .format = MPMarshalFormatNone, .identicon = MPIdenticonUnset };
 | 
			
		||||
    if (in && strlen( in )) {
 | 
			
		||||
        if (in[0] == '#') {
 | 
			
		||||
            info->format = MPMarshalFormatFlat;
 | 
			
		||||
@@ -1489,7 +1387,7 @@ MPMarshalInfo *mpw_marshal_read_info(
 | 
			
		||||
MPMarshalledFile *mpw_marshal_read(
 | 
			
		||||
        const char *in, const MPMasterKeyProvider masterKeyProvider, MPMarshalError *error) {
 | 
			
		||||
 | 
			
		||||
    MPMarshalInfo *info = mpw_marshal_read_info( in );
 | 
			
		||||
    MPMarshalledInfo *info = mpw_marshal_read_info( in );
 | 
			
		||||
    if (!info)
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,20 @@ typedef struct MPMarshalError {
 | 
			
		||||
    const char *message;
 | 
			
		||||
} MPMarshalError;
 | 
			
		||||
 | 
			
		||||
typedef struct MPMarshalInfo {
 | 
			
		||||
typedef struct MPMarshalledData {
 | 
			
		||||
    const char *obj_key;
 | 
			
		||||
    size_t arr_index;
 | 
			
		||||
 | 
			
		||||
    bool is_null;
 | 
			
		||||
    bool is_bool;
 | 
			
		||||
    const char *str_value;
 | 
			
		||||
    double num_value;
 | 
			
		||||
 | 
			
		||||
    size_t children_count;
 | 
			
		||||
    struct MPMarshalledData *children;
 | 
			
		||||
} MPMarshalledData;
 | 
			
		||||
 | 
			
		||||
typedef struct MPMarshalledInfo {
 | 
			
		||||
    MPMarshalFormat format;
 | 
			
		||||
    time_t exportDate;
 | 
			
		||||
    bool redacted;
 | 
			
		||||
@@ -80,7 +93,7 @@ typedef struct MPMarshalInfo {
 | 
			
		||||
    MPIdenticon identicon;
 | 
			
		||||
    const char *keyID;
 | 
			
		||||
    time_t lastUsed;
 | 
			
		||||
} MPMarshalInfo;
 | 
			
		||||
} MPMarshalledInfo;
 | 
			
		||||
 | 
			
		||||
typedef struct MPMarshalledQuestion {
 | 
			
		||||
    const char *keyword;
 | 
			
		||||
@@ -123,28 +136,8 @@ typedef struct MPMarshalledUser {
 | 
			
		||||
    MPMarshalledSite *sites;
 | 
			
		||||
} MPMarshalledUser;
 | 
			
		||||
 | 
			
		||||
typedef struct MPMarshalledData {
 | 
			
		||||
    // If data is held in a parent object.
 | 
			
		||||
    const char *key;
 | 
			
		||||
    // If data is held in a parent array.
 | 
			
		||||
    size_t index;
 | 
			
		||||
 | 
			
		||||
    // If data is a null.
 | 
			
		||||
    bool is_null;
 | 
			
		||||
    // If data is a boolean.
 | 
			
		||||
    bool is_bool;
 | 
			
		||||
    // If data is a string.
 | 
			
		||||
    const char *str_value;
 | 
			
		||||
    // If data is a number.
 | 
			
		||||
    double num_value;
 | 
			
		||||
 | 
			
		||||
    // If data is an object or array.
 | 
			
		||||
    size_t children_count;
 | 
			
		||||
    struct MPMarshalledData *children;
 | 
			
		||||
} MPMarshalledData;
 | 
			
		||||
 | 
			
		||||
typedef struct MPMarshalledFile {
 | 
			
		||||
    MPMarshalInfo *info;
 | 
			
		||||
    MPMarshalledInfo *info;
 | 
			
		||||
    MPMarshalledUser *user;
 | 
			
		||||
    MPMarshalledData *data;
 | 
			
		||||
} MPMarshalledFile;
 | 
			
		||||
@@ -157,7 +150,7 @@ const char *mpw_marshal_write(
 | 
			
		||||
        const MPMarshalFormat outFormat, 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(
 | 
			
		||||
MPMarshalledInfo *mpw_marshal_read_info(
 | 
			
		||||
        const char *in);
 | 
			
		||||
/** Unmarshall sites in the given input buffer by parsing it using the given marshalling format.
 | 
			
		||||
 * @return A user object (allocated), or NULL if the format provides no marshalling or a format error occurred. */
 | 
			
		||||
@@ -182,13 +175,13 @@ MPMarshalledQuestion *mpw_marshal_question(
 | 
			
		||||
/** Create or update a marshal file descriptor.
 | 
			
		||||
 * @return The given file or new (allocated) if file is NULL; or NULL if the user is missing or the file couldn't be allocated. */
 | 
			
		||||
MPMarshalledFile *mpw_marshal_file(
 | 
			
		||||
        MPMarshalledFile *const file, MPMarshalledUser *user, MPMarshalledData *data, MPMarshalInfo *info);
 | 
			
		||||
        MPMarshalledFile *const file, MPMarshalledUser *user, MPMarshalledData *data, MPMarshalledInfo *info);
 | 
			
		||||
 | 
			
		||||
//// Disposing.
 | 
			
		||||
 | 
			
		||||
/** Free the given user object and all associated data. */
 | 
			
		||||
void mpw_marshal_info_free(
 | 
			
		||||
        MPMarshalInfo **info);
 | 
			
		||||
        MPMarshalledInfo **info);
 | 
			
		||||
void mpw_marshal_user_free(
 | 
			
		||||
        MPMarshalledUser **user);
 | 
			
		||||
void mpw_marshal_data_free(
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user