/* SPDX-License-Identifier: BSD-2-Clause */
/*******************************************************************************
 * Copyright 2018, Fraunhofer SIT sponsored by Infineon Technologies AG
 * All rights reserved.
 ******************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h" // IWYU pragma: keep
#endif

#include <inttypes.h> // for uint8_t
#include <json.h>     // for json_object_object_del, json_objec...
#include <stdio.h>    // for NULL, size_t
#include <stdlib.h>   // for getenv
#include <string.h>   // for strlen, strncmp, strdup

#include "../helper/cmocka_all.h" // for assert_ptr_not_equal, assert_true
#include "ifapi_config.h"         // for IFAPI_CONFIG, ifapi_config_initial...
#include "ifapi_io.h"             // for IFAPI_IO
#include "tss2_common.h"          // for TSS2_FAPI_RC_BAD_VALUE, TSS2_RC
#include "util/aux_util.h"        // for SAFE_FREE

#define LOGMODULE tests
#include "util/log.h" // for LOG_WARNING

/*
 * The unit tests will test deserialization of FAPI config files. It will be
 * checked whether the correct return codes are returned if optional and
 * mandatory fields are removed from the configuration.
 * Also the expansion of abbreviations for the home directory will be
 * tested.
 */

/* Config file which will be used for the test. */
char *wrap_config_file_content;
/* JSON field which will be removed for the test. */
char *wrap_remove_field;

static char *config_tilde = "{"
                            "     \"profile_name\": \"P_ECCP256SHA256\","
                            "     \"profile_dir\": \"~/profile\","
                            "     \"user_dir\": \"~/user_dir\","
                            "     \"system_dir\": \"~/system_dir\","
                            "     \"log_dir\": \"~/log_dir\","
                            "     \"tcti\": \"\","
                            "     \"system_pcrs\" : []"
                            "}";

static char *config_home = "{"
                           "     \"profile_name\": \"P_ECCP256SHA256\","
                           "     \"profile_dir\": \"$HOME/profile\","
                           "     \"user_dir\": \"$HOME/user_dir\","
                           "     \"system_dir\": \"$HOME/system_dir\","
                           "     \"log_dir\": \"$HOME/log_dir\","
                           "     \"tcti\": \"\","
                           "     \"system_pcrs\" : []"
                           "}";

/*
 * Wrappers for reading the JSON profile.
 */
TSS2_RC
__wrap_ifapi_io_read_finish(struct IFAPI_IO *io, uint8_t **buffer, size_t *length, ...);

TSS2_RC
__wrap_ifapi_io_read_finish(struct IFAPI_IO *io, uint8_t **buffer, size_t *length, ...) {
    json_object *jso = NULL;
    const char  *jso_string = NULL;

    jso = json_tokener_parse(wrap_config_file_content);
    assert_ptr_not_equal(jso, NULL);
    json_object_object_del(jso, wrap_remove_field);
    jso_string = json_object_to_json_string_ext(jso, JSON_C_TO_STRING_PRETTY);
    assert_ptr_not_equal(jso_string, NULL);
    *buffer = (uint8_t *)strdup(jso_string);
    *length = strlen(jso_string);
    json_object_put(jso);
    assert_ptr_not_equal(*buffer, NULL);
    return TSS2_RC_SUCCESS;
}

/* Function to remove the field and check the initialization of the configuration. */
void
check_remove_field(char *file_content, char *fname, TSS2_RC rc) {
    IFAPI_IO     io;
    IFAPI_CONFIG config;
    TSS2_RC      r;
    char        *home_dir = getenv("HOME");

    assert_ptr_not_equal(home_dir, NULL);
    if (home_dir == NULL)
        return; // This is for clang-tidy which complains about following strlen() otherwise
    wrap_config_file_content = file_content;
    wrap_remove_field = fname;
    r = ifapi_config_initialize_finish(&io, &config);
    assert_int_equal(r, rc);
    if (r == TSS2_RC_SUCCESS) {
        LOG_WARNING("TEST OUTPUT: %s", config.profile_dir);
        assert_true(strncmp(config.profile_dir, home_dir, strlen(home_dir)) == 0);
        SAFE_FREE(config.profile_dir);
        assert_true(strncmp(config.user_dir, home_dir, strlen(home_dir)) == 0);
        SAFE_FREE(config.user_dir);
        assert_true(strncmp(config.keystore_dir, home_dir, strlen(home_dir)) == 0);
        SAFE_FREE(config.keystore_dir);
        SAFE_FREE(config.log_dir);
        SAFE_FREE(config.profile_name);
        SAFE_FREE(config.tcti);
        SAFE_FREE(config.ek_cert_file);
        SAFE_FREE(config.web_cert_service)
    }
}

/* Function to remove the field and check the initialization of the configuration. */
static void
check_config_json_remove_field_allowed(void **state) {
    check_remove_field(config_home, "log_dir", TSS2_RC_SUCCESS);
    check_remove_field(config_tilde, "log_dir", TSS2_RC_SUCCESS);
}

/* Check removing of the mandatory fields. */
static void
check_config_json_remove_field_not_allowed(void **state) {
    check_remove_field(config_tilde, "profile_dir", TSS2_FAPI_RC_BAD_VALUE);
    check_remove_field(config_tilde, "system_dir", TSS2_FAPI_RC_BAD_VALUE);
    check_remove_field(config_tilde, "user_dir", TSS2_FAPI_RC_BAD_VALUE);
    check_remove_field(config_tilde, "profile_name", TSS2_FAPI_RC_BAD_VALUE);
    check_remove_field(config_tilde, "tcti", TSS2_FAPI_RC_BAD_VALUE);
    check_remove_field(config_tilde, "system_pcrs", TSS2_FAPI_RC_BAD_VALUE);
}

int
main(int argc, char *argv[]) {
    const struct CMUnitTest tests[] = {
        cmocka_unit_test(check_config_json_remove_field_allowed),
        cmocka_unit_test(check_config_json_remove_field_not_allowed),
    };
    return cmocka_run_group_tests(tests, NULL, NULL);
}
