/**
 * @file oval_resultTest.c
 * \brief Open Vulnerability and Assessment Language
 *
 * See more details at http://oval.mitre.org/
 */

/*
 * Copyright 2009--2014 Red Hat Inc., Durham, North Carolina.
 * All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 * Authors:
 *      "David Niemoller" <David.Niemoller@g2-inc.com>
 *      "Peter Vrabec" <pvrabec@redhat.com>
 *      Šimon Lukašík
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>
#include <ctype.h>
#include "oval_agent_api_impl.h"
#ifdef OVAL_PROBES_ENABLED
#include "oval_probe_impl.h"
#endif
#include "results/oval_results_impl.h"
#include "results/oval_status_counter.h"
#include "oval_cmp_impl.h"
#include "adt/oval_collection_impl.h"
#include "adt/oval_string_map_impl.h"
#include "collectVarRefs_impl.h"
#include "public/oval_types.h"
#include "common/util.h"
#include "common/debug_priv.h"
#include "common/_error.h"

typedef struct oval_result_test {
	struct oval_result_system *system;
	struct oval_test *test;
	oval_result_t result;
	struct oval_collection *messages;
	struct oval_collection *items;
	struct oval_collection *bindings;
	int instance;
	bool bindings_initialized;
} oval_result_test_t;

struct oval_result_test *oval_result_test_new(struct oval_result_system *sys, char *tstid)
{
	oval_result_test_t *test = (oval_result_test_t *)
	    malloc(sizeof(oval_result_test_t));
	if (test == NULL)
		return NULL;

	struct oval_syschar_model *syschar_model = oval_result_system_get_syschar_model(sys);
	struct oval_definition_model *definition_model = oval_syschar_model_get_definition_model(syschar_model);
	test->system = sys;
	test->test = oval_definition_model_get_new_test(definition_model, tstid);
	test->messages = oval_collection_new();
	test->result = OVAL_RESULT_NOT_EVALUATED;
	test->instance = 1;
	test->items = oval_collection_new();
	test->bindings = oval_collection_new();
	test->bindings_initialized = false;
	return test;
}

struct oval_result_test *oval_result_test_clone
    (struct oval_result_system *new_system, struct oval_result_test *old_test) {
	__attribute__nonnull__(old_test);

	const char *testid = oval_result_test_get_id(old_test);
	struct oval_result_test *new_test = oval_result_test_new(new_system, (char *) testid);
	struct oval_result_item_iterator *old_items = oval_result_test_get_items(old_test);
	while (oval_result_item_iterator_has_more(old_items)) {
		struct oval_result_item *old_item = oval_result_item_iterator_next(old_items);
		struct oval_result_item *new_item = oval_result_item_clone(new_system, old_item);
		oval_result_test_add_item(new_test, new_item);
	}
	oval_result_item_iterator_free(old_items);

	struct oval_variable_binding_iterator *old_bindings = oval_result_test_get_bindings(old_test);
	while (oval_variable_binding_iterator_has_more(old_bindings)) {
		struct oval_variable_binding *old_binding = oval_variable_binding_iterator_next(old_bindings);
		struct oval_variable_binding *new_binding = oval_variable_binding_clone(old_binding, NULL);
		oval_result_test_add_binding(new_test, new_binding);
	}
	oval_variable_binding_iterator_free(old_bindings);

	struct oval_message_iterator *old_messages = oval_result_test_get_messages(old_test);
	while (oval_message_iterator_has_more(old_messages)) {
		struct oval_message *old_message = oval_message_iterator_next(old_messages);
		struct oval_message *new_message = oval_message_clone(old_message);
		oval_result_test_add_message(new_test, new_message);
	}
	oval_message_iterator_free(old_messages);

	oval_result_test_set_instance(new_test, oval_result_test_get_instance(old_test));
	oval_result_test_set_result(new_test, old_test->result);

	return new_test;
}

struct oval_result_test *make_result_test_from_oval_test(struct oval_result_system *sys, struct oval_test *oval_test, int variable_instance) {
	char *test_id = oval_test_get_id(oval_test);
	struct oval_result_test *test = oval_result_test_new(sys, test_id);
	oval_result_test_set_instance(test, variable_instance);
	return test;
}

void oval_result_test_free(struct oval_result_test *test)
{
	__attribute__nonnull__(test);

	oval_collection_free_items(test->messages, (oscap_destruct_func) oval_message_free);
	oval_collection_free_items(test->items, (oscap_destruct_func) oval_result_item_free);
	oval_collection_free_items(test->bindings, (oscap_destruct_func) oval_variable_binding_free);

	test->system = NULL;
	test->test = NULL;
	test->messages = NULL;
	test->result = OVAL_RESULT_NOT_EVALUATED;
	test->items = NULL;
	test->bindings = NULL;
	test->instance = 1;
	free(test);
}

struct oval_result_system *oval_result_test_get_system(struct oval_result_test *rtest)
{
	__attribute__nonnull__(rtest);

	return rtest->system;
}

struct oval_test *oval_result_test_get_test(struct oval_result_test *rtest)
{
	__attribute__nonnull__(rtest);

	return ((struct oval_result_test *)rtest)->test;
}

int ores_add_res(struct oresults *ores, oval_result_t res)
{
		switch (res) {
		case OVAL_RESULT_TRUE:
			ores->true_cnt++;
			break;
		case OVAL_RESULT_FALSE:
			ores->false_cnt++;
			break;
		case OVAL_RESULT_UNKNOWN:
			ores->unknown_cnt++;
			break;
		case OVAL_RESULT_ERROR:
			ores->error_cnt++;
			break;
		case OVAL_RESULT_NOT_EVALUATED:
			ores->noteval_cnt++;
			break;
		case OVAL_RESULT_NOT_APPLICABLE:
			ores->notappl_cnt++;
			break;
		default:
			oscap_seterr(OSCAP_EFAMILY_OSCAP, "Invalid oval result type: %d.", res);
			return 1;
		}

		return 0;
}

void ores_clear(struct oresults *ores)
{
	memset(ores, 0, sizeof (*ores));
}

oval_result_t ores_get_result_bychk(struct oresults *ores, oval_check_t check)
{
	oval_result_t result = OVAL_RESULT_ERROR;

	if (ores->true_cnt == 0 &&
	    ores->false_cnt == 0 &&
	    ores->error_cnt == 0 &&
	    ores->unknown_cnt == 0 &&
	    ores->notappl_cnt == 0 &&
	    ores->noteval_cnt == 0)
		return OVAL_RESULT_UNKNOWN;

	if (ores->notappl_cnt > 0 &&
	    ores->noteval_cnt == 0 &&
	    ores->false_cnt == 0 && ores->error_cnt == 0 && ores->unknown_cnt == 0 && ores->true_cnt == 0)
		return OVAL_RESULT_NOT_APPLICABLE;

	switch (check) {
	case OVAL_CHECK_ALL:
		if (ores->true_cnt > 0 &&
		    ores->false_cnt == 0 && ores->error_cnt == 0 && ores->unknown_cnt == 0 && ores->noteval_cnt == 0) {
			result = OVAL_RESULT_TRUE;
		} else if (ores->false_cnt > 0) {
			result = OVAL_RESULT_FALSE;
		} else if (ores->false_cnt == 0 && ores->error_cnt > 0) {
			result = OVAL_RESULT_ERROR;
		} else if (ores->false_cnt == 0 && ores->error_cnt == 0 && ores->unknown_cnt > 0) {
			result = OVAL_RESULT_UNKNOWN;
		} else if (ores->false_cnt == 0 && ores->error_cnt == 0 && ores->unknown_cnt == 0 && ores->noteval_cnt > 0) {
			result = OVAL_RESULT_NOT_EVALUATED;
		}
		break;
	case OVAL_CHECK_AT_LEAST_ONE:
		if (ores->true_cnt > 0) {
			result = OVAL_RESULT_TRUE;
		} else if (ores->false_cnt > 0 &&
			   ores->true_cnt == 0 &&
			   ores->unknown_cnt == 0 && ores->error_cnt == 0 && ores->noteval_cnt == 0) {
			result = OVAL_RESULT_FALSE;
		} else if (ores->true_cnt == 0 && ores->error_cnt > 0) {
			result = OVAL_RESULT_ERROR;
		} else if (ores->false_cnt == 0 && ores->error_cnt == 0 && ores->unknown_cnt > 0) {
			result = OVAL_RESULT_UNKNOWN;
		} else if (ores->false_cnt == 0 && ores->error_cnt == 0 && ores->unknown_cnt == 0 && ores->noteval_cnt > 0) {
			result = OVAL_RESULT_NOT_EVALUATED;
		}
		break;
	case OVAL_CHECK_NONE_EXIST:
		dW("The 'none exist' CheckEnumeration value has been deprecated. "
		   "Converted to check='none satisfy'.");
		/* FALLTHROUGH */
	case OVAL_CHECK_NONE_SATISFY:
		if (ores->true_cnt > 0) {
			result = OVAL_RESULT_FALSE;
		} else if (ores->true_cnt == 0 && ores->error_cnt > 0) {
			result = OVAL_RESULT_ERROR;
		} else if (ores->true_cnt == 0 && ores->error_cnt == 0 && ores->unknown_cnt > 0) {
			result = OVAL_RESULT_UNKNOWN;
		} else if (ores->true_cnt == 0 && ores->error_cnt == 0 && ores->unknown_cnt == 0 && ores->noteval_cnt > 0) {
			result = OVAL_RESULT_NOT_EVALUATED;
		} else if (ores->false_cnt > 0 &&
			   ores->error_cnt == 0 &&
			   ores->unknown_cnt == 0 && ores->noteval_cnt == 0 && ores->true_cnt == 0) {
			result = OVAL_RESULT_TRUE;
		}
		break;
	case OVAL_CHECK_ONLY_ONE:
		if (ores->true_cnt == 1 && ores->error_cnt == 0 && ores->unknown_cnt == 0 && ores->noteval_cnt == 0) {
			result = OVAL_RESULT_TRUE;
		} else if (ores->true_cnt > 1) {
			result = OVAL_RESULT_FALSE;
		} else if (ores->true_cnt < 2 && ores->error_cnt > 0) {
			result = OVAL_RESULT_ERROR;
		} else if (ores->true_cnt < 2 && ores->error_cnt == 0 && ores->unknown_cnt > 0) {
			result = OVAL_RESULT_UNKNOWN;
		} else if (ores->true_cnt < 2 && ores->error_cnt == 0 && ores->unknown_cnt == 0 && ores->noteval_cnt > 0) {
			result = OVAL_RESULT_NOT_EVALUATED;
		} else if (ores->true_cnt != 1 && ores->false_cnt > 0) {
			result = OVAL_RESULT_FALSE;
		}
		break;
	default:
                oscap_seterr(OSCAP_EFAMILY_OSCAP, "Invalid check value: %d.", check);
		result = OVAL_RESULT_ERROR;
	}

	return result;
}

oval_result_t ores_get_result_byopr(struct oresults *ores, oval_operator_t op)
{
	oval_result_t result = OVAL_RESULT_ERROR;

	if (ores->true_cnt == 0 &&
	    ores->false_cnt == 0 &&
	    ores->error_cnt == 0 &&
	    ores->unknown_cnt == 0 &&
	    ores->notappl_cnt == 0 &&
	    ores->noteval_cnt == 0)
		return OVAL_RESULT_UNKNOWN;

	if (ores->notappl_cnt > 0 &&
	    ores->noteval_cnt == 0 &&
	    ores->false_cnt == 0 && ores->error_cnt == 0 && ores->unknown_cnt == 0 && ores->true_cnt == 0)
		return OVAL_RESULT_NOT_APPLICABLE;

	switch (op) {
	case OVAL_OPERATOR_AND:
		if (ores->true_cnt > 0 &&
		    ores->false_cnt == 0 && ores->error_cnt == 0 && ores->unknown_cnt == 0 && ores->noteval_cnt == 0) {
			result = OVAL_RESULT_TRUE;
		} else if (ores->false_cnt > 0) {
			result = OVAL_RESULT_FALSE;
		} else if (ores->false_cnt == 0 && ores->error_cnt > 0) {
			result = OVAL_RESULT_ERROR;
		} else if (ores->false_cnt == 0 && ores->error_cnt == 0 && ores->unknown_cnt > 0) {
			result = OVAL_RESULT_UNKNOWN;
		} else if (ores->false_cnt == 0 && ores->error_cnt == 0 && ores->unknown_cnt == 0 && ores->noteval_cnt > 0) {
			result = OVAL_RESULT_NOT_EVALUATED;
		}
		break;
	case OVAL_OPERATOR_ONE:
		if (ores->true_cnt == 1 &&
		    ores->false_cnt >= 0 &&
		    ores->error_cnt == 0 && ores->unknown_cnt == 0 && ores->noteval_cnt == 0 && ores->notappl_cnt >= 0) {
			result = OVAL_RESULT_TRUE;
		} else if (ores->true_cnt >= 2 &&
			   ores->false_cnt >= 0 &&
			   ores->error_cnt >= 0 &&
			   ores->unknown_cnt >= 0 && ores->noteval_cnt >= 0 && ores->notappl_cnt >= 0) {
			result = OVAL_RESULT_FALSE;
		} else if (ores->true_cnt == 0 &&
			   ores->false_cnt >= 0 &&
			   ores->error_cnt == 0 &&
			   ores->unknown_cnt == 0 && ores->noteval_cnt == 0 && ores->notappl_cnt >= 0) {
			result = OVAL_RESULT_FALSE;
		} else if (ores->true_cnt < 2 &&
			   ores->false_cnt >= 0 &&
			   ores->error_cnt > 0 &&
			   ores->unknown_cnt >= 0 && ores->noteval_cnt >= 0 && ores->notappl_cnt >= 0) {
			result = OVAL_RESULT_ERROR;
		} else if (ores->true_cnt < 2 &&
			   ores->false_cnt >= 0 &&
			   ores->error_cnt == 0 &&
			   ores->unknown_cnt >= 1 && ores->noteval_cnt >= 0 && ores->notappl_cnt >= 0) {
			result = OVAL_RESULT_UNKNOWN;
		} else if (ores->true_cnt < 2 &&
			   ores->false_cnt >= 0 &&
			   ores->error_cnt == 0 &&
			   ores->unknown_cnt == 0 && ores->noteval_cnt > 0 && ores->notappl_cnt >= 0) {
			result = OVAL_RESULT_NOT_EVALUATED;
		}
		break;
	case OVAL_OPERATOR_OR:
		if (ores->true_cnt > 0) {
			result = OVAL_RESULT_TRUE;
		} else if (ores->true_cnt == 0 &&
			   ores->false_cnt > 0 &&
			   ores->error_cnt == 0 && ores->unknown_cnt == 0 && ores->noteval_cnt == 0) {
			result = OVAL_RESULT_FALSE;
		} else if (ores->true_cnt == 0 && ores->error_cnt > 0) {
			result = OVAL_RESULT_ERROR;
		} else if (ores->true_cnt == 0 && ores->error_cnt == 0 && ores->unknown_cnt > 0) {
			result = OVAL_RESULT_UNKNOWN;
		} else if (ores->true_cnt == 0 && ores->error_cnt == 0 && ores->unknown_cnt == 0 && ores->noteval_cnt > 0) {
			result = OVAL_RESULT_NOT_EVALUATED;
		}
		break;
	case OVAL_OPERATOR_XOR:
		if ((ores->true_cnt % 2) == 1 && ores->error_cnt == 0 && ores->unknown_cnt == 0 && ores->noteval_cnt == 0) {
			result = OVAL_RESULT_TRUE;
		} else if ((ores->true_cnt % 2) == 0 &&
			   ores->error_cnt == 0 && ores->unknown_cnt == 0 && ores->noteval_cnt == 0) {
			result = OVAL_RESULT_FALSE;
		} else if (ores->error_cnt > 0) {
			result = OVAL_RESULT_ERROR;
		} else if (ores->error_cnt == 0 && ores->unknown_cnt > 0) {
			result = OVAL_RESULT_UNKNOWN;
		} else if (ores->error_cnt == 0 && ores->unknown_cnt == 0 && ores->noteval_cnt > 0) {
			result = OVAL_RESULT_NOT_EVALUATED;
		}
		break;
	default:
                oscap_seterr(OSCAP_EFAMILY_OSCAP, "Invalid operator value: %d.", op);
		result = OVAL_RESULT_ERROR;
		break;
	}

	return result;
}

static inline oval_result_t _evaluate_sysent_with_variable(struct oval_syschar_model *syschar_model, struct oval_variable *state_entity_var, const char *sys_data, oval_operation_t state_entity_operation, oval_check_t var_check)
{
	oval_syschar_collection_flag_t flag;
	oval_result_t ent_val_res;

	if (0 != oval_syschar_model_compute_variable(syschar_model, state_entity_var)) {
		return -1;
	}

	flag = oval_variable_get_collection_flag(state_entity_var);
	switch (flag) {
	case SYSCHAR_FLAG_COMPLETE:
	case SYSCHAR_FLAG_INCOMPLETE:{
		struct oresults var_ores;
		struct oval_value_iterator *val_itr;

		ores_clear(&var_ores);

		val_itr = oval_variable_get_values(state_entity_var);
		while (oval_value_iterator_has_more(val_itr)) {
			struct oval_value *var_val;
			char *state_entity_val_text = NULL;
			oval_result_t var_val_res;

			var_val = oval_value_iterator_next(val_itr);
			state_entity_val_text = oval_value_get_text(var_val);
			if (state_entity_val_text == NULL) {
				dE("Found NULL variable value text.");
				ores_add_res(&var_ores, OVAL_RESULT_ERROR);
				break;
			}
			oval_datatype_t state_entity_val_datatype = oval_value_get_datatype(var_val);

			var_val_res = oval_str_cmp_str(state_entity_val_text, state_entity_val_datatype, sys_data, state_entity_operation);
			if (var_val_res == OVAL_RESULT_ERROR) {
				dW("Can't compare variable '%s' value = '%s' with collected item entity = '%s'",
					oval_variable_get_id(state_entity_var), state_entity_val_text, sys_data);
			}
			ores_add_res(&var_ores, var_val_res);
		}
		oval_value_iterator_free(val_itr);

		ent_val_res = ores_get_result_bychk(&var_ores, var_check);
		} break;
	case SYSCHAR_FLAG_ERROR:
	case SYSCHAR_FLAG_DOES_NOT_EXIST:
	case SYSCHAR_FLAG_NOT_COLLECTED:
	case SYSCHAR_FLAG_NOT_APPLICABLE:
		ent_val_res = OVAL_RESULT_ERROR;
		break;
	default:
		ent_val_res = -1;
	}

	return ent_val_res;
}

struct record_field_instance {
	char *name;
	char *value;
	oval_datatype_t data_type;
	oval_check_t ent_check;
	oval_operation_t operation;
	struct oval_variable *var;
	oval_check_t var_check;
};

static struct record_field_instance _oval_record_field_iterator_next_instance(struct oval_record_field_iterator *iterator)
{
	struct record_field_instance instance;
	struct oval_record_field *rf = oval_record_field_iterator_next(iterator);
	instance.name = oval_record_field_get_name(rf);
	instance.value = oval_record_field_get_value(rf);
	instance.data_type = oval_record_field_get_datatype(rf);
	if (oval_record_field_get_type(rf) == OVAL_RECORD_FIELD_STATE) {
		instance.ent_check = oval_record_field_get_ent_check(rf);
		instance.operation = oval_record_field_get_operation(rf);
		instance.var = oval_record_field_get_variable(rf);
		instance.var_check = oval_record_field_get_var_check(rf);
	}
	return instance;
}

static oval_result_t _evaluate_sysent_record(struct oval_syschar_model *syschar_model, struct oval_state_content *state_content, struct oval_sysent *item_entity)
{
	struct oresults record_ores;
	ores_clear(&record_ores);
	/* During analysis of a system characteristics item, each record field is
	 * analyzed and then the overall result for elements of the record type is
	 * computed by logically ANDing the results for each field and applying
	 * the entity_check attribute.
	 */
	struct oval_record_field_iterator *state_it = oval_state_content_get_record_fields(state_content);
	while (oval_record_field_iterator_has_more(state_it)) {
		struct record_field_instance state_rf = _oval_record_field_iterator_next_instance(state_it);
		bool field_found = false;
		struct oresults field_ores;
		ores_clear(&field_ores);
		struct oval_record_field_iterator *item_it = oval_sysent_get_record_fields(item_entity);
		while (oval_record_field_iterator_has_more(item_it)) {
			struct record_field_instance item_rf = _oval_record_field_iterator_next_instance(item_it);
			if (strcmp(state_rf.name, item_rf.name) == 0) {
				field_found = true;
				oval_result_t fields_comparison_result;
				if (state_rf.var != NULL) {
					fields_comparison_result = _evaluate_sysent_with_variable(syschar_model, state_rf.var, item_rf.value, state_rf.operation, state_rf.var_check);
				} else {
					fields_comparison_result = oval_str_cmp_str(state_rf.value, state_rf.data_type, item_rf.value, state_rf.operation);
				}
				ores_add_res(&field_ores, fields_comparison_result);
			}
		}
		oval_record_field_iterator_free(item_it);
		/* When analyzing system characteristics an error should be reported
		 * for the result of a field that is present in the OVAL State, but
		 * not found in the system characteristics item.
		 */
		if (!field_found) {
			ores_add_res(&record_ores, OVAL_RESULT_ERROR);
		} else {
			oval_result_t field_result = ores_get_result_bychk(&field_ores, state_rf.ent_check);
			ores_add_res(&record_ores, field_result);
		}
	}
	oval_record_field_iterator_free(state_it);
	return ores_get_result_byopr(&record_ores, OVAL_OPERATOR_AND);
}

static inline oval_result_t _evaluate_sysent(struct oval_syschar_model *syschar_model, struct oval_sysent *item_entity, struct oval_entity *state_entity, oval_operation_t state_entity_operation, struct oval_state_content *content)
{
	if (oval_sysent_get_status(item_entity) == SYSCHAR_STATUS_DOES_NOT_EXIST) {
		return OVAL_RESULT_FALSE;
	} else if (oval_entity_get_varref_type(state_entity) == OVAL_ENTITY_VARREF_ATTRIBUTE) {
		struct oval_variable *state_entity_var;
		if ((state_entity_var = oval_entity_get_variable(state_entity)) == NULL) {
			oscap_seterr(OSCAP_EFAMILY_OVAL, "OVAL internal error: found NULL variable");
			return -1;
		}
		const char *sys_data = oval_sysent_get_value(item_entity);
		oval_check_t var_check = oval_state_content_get_var_check(content);

		return _evaluate_sysent_with_variable(syschar_model,
				state_entity_var, sys_data,
				state_entity_operation, var_check);
	} else {
		struct oval_value *state_entity_val;
		char *state_entity_val_text;
		oval_datatype_t state_entity_val_datatype;

		oval_datatype_t state_entity_type = oval_entity_get_datatype(state_entity);
		if (state_entity_type == OVAL_DATATYPE_RECORD) {
			if (state_entity_operation != OVAL_OPERATION_EQUALS) {
				dE("The only allowed operation for comparing record types is 'equals'.");
				return OVAL_RESULT_ERROR;
			}
			return _evaluate_sysent_record(syschar_model, content, item_entity);
		} else {
			if ((state_entity_val = oval_entity_get_value(state_entity)) == NULL) {
				oscap_seterr(OSCAP_EFAMILY_OVAL, "OVAL internal error: found NULL entity value");
				return -1;
			}
			if ((state_entity_val_text = oval_value_get_text(state_entity_val)) == NULL) {
				oscap_seterr(OSCAP_EFAMILY_OVAL, "OVAL internal error: found NULL entity value text");
				return -1;
			}
			state_entity_val_datatype = oval_value_get_datatype(state_entity_val);

			const char *sys_data = oval_sysent_get_value(item_entity);
			return oval_str_cmp_str(state_entity_val_text, state_entity_val_datatype, sys_data, state_entity_operation);
		}
	}
}

static oval_result_t eval_item(struct oval_syschar_model *syschar_model, struct oval_sysitem *cur_sysitem, struct oval_state *state)
{
	struct oval_state_content_iterator *state_contents_itr;
	struct oresults ste_ores;
	oval_operator_t operator;
	oval_result_t result = OVAL_RESULT_ERROR;

	ores_clear(&ste_ores);

	state_contents_itr = oval_state_get_contents(state);
	while (oval_state_content_iterator_has_more(state_contents_itr)) {
		struct oval_state_content *content;
		struct oval_entity *state_entity;
		char *state_entity_name;
		oval_operation_t state_entity_operation;
		oval_check_t entity_check;
		oval_existence_t check_existence;
		oval_result_t ste_ent_res;
		struct oval_sysent_iterator *item_entities_itr;
		struct oresults ent_ores;
		struct oval_status_counter counter;
		bool found_matching_item;

		if ((content = oval_state_content_iterator_next(state_contents_itr)) == NULL) {
			oscap_seterr(OSCAP_EFAMILY_OVAL, "OVAL internal error: found NULL state content");
			goto fail;
		}
		if ((state_entity = oval_state_content_get_entity(content)) == NULL) {
			oscap_seterr(OSCAP_EFAMILY_OVAL, "OVAL internal error: found NULL entity");
			goto fail;
		}
		if ((state_entity_name = oval_entity_get_name(state_entity)) == NULL) {
			oscap_seterr(OSCAP_EFAMILY_OVAL, "OVAL internal error: found NULL entity name");
			goto fail;
		}

		if (oscap_streq(state_entity_name, "line") &&
			oval_state_get_subtype(state) == (oval_subtype_t) OVAL_INDEPENDENT_TEXT_FILE_CONTENT) {
			/* Hack: textfilecontent_state/line shall be compared against textfilecontent_item/text.
			 *
			 * textfilecontent_test and textfilecontent54_test share the same syschar
			 * (textfilecontent_item). In OVAL 5.3 and below this syschar did not hold any usable
			 * information ('text' ent). In OVAL 5.4 textfilecontent_test was deprecated. But the
			 * 'text' ent has been added to textfilecontent_item, making it potentially usable. */
			oval_schema_version_t over = oval_state_get_platform_schema_version(state);
			if (oval_schema_version_cmp(over, OVAL_SCHEMA_VERSION(5.4)) >= 0) {
				/* The OVAL-5.3 does not have textfilecontent_item/text */
				state_entity_name = "text";
			}
		}

		entity_check = oval_state_content_get_ent_check(content);
		check_existence = oval_state_content_get_check_existence(content);
		state_entity_operation = oval_entity_get_operation(state_entity);

		ores_clear(&ent_ores);
		found_matching_item = false;
		oval_status_counter_clear(&counter);

		item_entities_itr = oval_sysitem_get_sysents(cur_sysitem);
		while (oval_sysent_iterator_has_more(item_entities_itr)) {
			struct oval_sysent *item_entity;
			oval_result_t ent_val_res;
			char *item_entity_name;
			oval_syschar_status_t item_status;

			item_entity = oval_sysent_iterator_next(item_entities_itr);
			if (item_entity == NULL) {
				oscap_seterr(OSCAP_EFAMILY_OVAL, "OVAL internal error: found NULL sysent");
				oval_sysent_iterator_free(item_entities_itr);
				goto fail;
			}
			item_status = oval_sysent_get_status(item_entity);
			oval_status_counter_add_status(&counter, item_status);

			item_entity_name = oval_sysent_get_name(item_entity);
			if (strcmp(item_entity_name, state_entity_name))
				continue;

			found_matching_item = true;

			/* copy mask attribute from state to item */
			if (oval_entity_get_mask(state_entity))
				oval_sysent_set_mask(item_entity,1);

			ent_val_res = _evaluate_sysent(syschar_model, item_entity, state_entity,
					state_entity_operation, content);
			if (ent_val_res == OVAL_RESULT_TRUE) {
				dI("Entity '%s'='%s' of item '%s' matches corresponding entity in state '%s'.",
						oval_sysent_get_name(item_entity),
						oval_sysent_get_value(item_entity),
						oval_sysitem_get_id(cur_sysitem), oval_state_get_id(state));
			}
			if (ent_val_res == OVAL_RESULT_ERROR) {
				dI("Comparing entity '%s'='%s' of item '%s' to corresponding entity in state '%s' was not successful.",
						oval_sysent_get_name(item_entity),
						oval_sysent_get_value(item_entity),
						oval_sysitem_get_id(cur_sysitem), oval_state_get_id(state));
			}
			if (((signed) ent_val_res) == -1) {
				oval_sysent_iterator_free(item_entities_itr);
				goto fail;
			}

			ores_add_res(&ent_ores, ent_val_res);
		}
		oval_sysent_iterator_free(item_entities_itr);

		if (!found_matching_item)
			dW("Entity name '%s' from state (id: '%s') not found in item (id: '%s').",
			   state_entity_name, oval_state_get_id(state), oval_sysitem_get_id(cur_sysitem));

		oval_result_t cres = oval_status_counter_get_result(&counter, check_existence);
		/* The entity check results are only relevant when the check existence is satisfied */
		if (cres == OVAL_RESULT_TRUE) {
			ste_ent_res = ores_get_result_bychk(&ent_ores, entity_check);
			ores_add_res(&ste_ores, ste_ent_res);
		} else {
			ores_add_res(&ste_ores, cres);
		}
	}
	oval_state_content_iterator_free(state_contents_itr);

	operator = oval_state_get_operator(state);
	result = ores_get_result_byopr(&ste_ores, operator);
	dI("Item '%s' compared to state '%s' with result %s.",
			   oval_sysitem_get_id(cur_sysitem), oval_state_get_id(state),
			   oval_result_get_text(result));

	return result;

 fail:
	oval_state_content_iterator_free(state_contents_itr);

	return OVAL_RESULT_ERROR;
}

#define ITEMMAP (struct oval_string_map    *)args[2]
#define TEST    (struct oval_result_test   *)args[1]
#define SYSTEM  (struct oval_result_system *)args[0]

static void _oval_test_item_consumer(struct oval_result_item *item, void **args) {
	struct oval_sysitem *oval_sysitem = oval_result_item_get_sysitem(item);
	char *item_id = oval_sysitem_get_id(oval_sysitem);
	struct oval_result_item *mapped_item = oval_string_map_get_value(ITEMMAP, item_id);
	if (mapped_item == NULL) {
		oval_string_map_put(ITEMMAP, item_id, item);
		oval_result_test_add_item(TEST, item);
	} else {
		oval_result_item_free(item);
	}
}

static oval_result_t eval_check_state(struct oval_test *test, void **args)
{
	struct oval_syschar_model *syschar_model;
	struct oval_result_item_iterator *ritems_itr;
	struct oresults item_ores;
	oval_result_t result;
	oval_check_t ste_check;
	oval_operator_t ste_opr;

	ste_check = oval_test_get_check(test);
	ste_opr = oval_test_get_state_operator(test);
	syschar_model = oval_result_system_get_syschar_model(SYSTEM);
	ores_clear(&item_ores);

	char *state_names = oval_test_get_state_names(test);
	if (state_names) {
		dI("In test '%s' %s of the collected items must satisfy these states: %s.",
			oval_test_get_id(test), oval_check_get_description(ste_check), state_names);
		free(state_names);
	}

	ritems_itr = oval_result_test_get_items(TEST);
	while (oval_result_item_iterator_has_more(ritems_itr)) {
		struct oval_result_item *ritem;
		struct oval_sysitem *item;
		oval_syschar_status_t item_status;
		struct oresults ste_ores;
		struct oval_state_iterator *ste_itr;
		oval_result_t item_res;

		ritem = oval_result_item_iterator_next(ritems_itr);
		item = oval_result_item_get_sysitem(ritem);

		item_status = oval_sysitem_get_status(item);
		switch (item_status) {
		case SYSCHAR_STATUS_ERROR:
		case SYSCHAR_STATUS_NOT_COLLECTED:
			item_res = OVAL_RESULT_ERROR;
			ores_add_res(&item_ores, item_res);
			oval_result_item_set_result(ritem, item_res);
			continue;
		case SYSCHAR_STATUS_DOES_NOT_EXIST:
			item_res = OVAL_RESULT_FALSE;
			ores_add_res(&item_ores, item_res);
			oval_result_item_set_result(ritem, item_res);
			continue;
		default:
			break;
		}

		ores_clear(&ste_ores);

		ste_itr = oval_test_get_states(test);
		while (oval_state_iterator_has_more(ste_itr)) {
			struct oval_state *ste;
			oval_result_t ste_res;

			ste = oval_state_iterator_next(ste_itr);
			ste_res = eval_item(syschar_model, item, ste);
			ores_add_res(&ste_ores, ste_res);
		}
		oval_state_iterator_free(ste_itr);

		item_res = ores_get_result_byopr(&ste_ores, ste_opr);
		ores_add_res(&item_ores, item_res);
		oval_result_item_set_result(ritem, item_res);
	}
	oval_result_item_iterator_free(ritems_itr);

	result = ores_get_result_bychk(&item_ores, ste_check);

	return result;
}

static oval_result_t eval_check_existence(oval_existence_t check_existence, int exists_cnt, int error_cnt)
{
	oval_result_t result = OVAL_RESULT_ERROR;

	switch (check_existence) {
	case OVAL_ALL_EXIST:
		if (exists_cnt >= 1 && error_cnt == 0) {
			result = OVAL_RESULT_TRUE;
		} else if (exists_cnt == 0 && error_cnt == 0) {
			result = OVAL_RESULT_FALSE;
		} else if (exists_cnt >= 0 && error_cnt >= 1) {
			result = OVAL_RESULT_ERROR;
		}
		break;
	case OVAL_ANY_EXIST:
		if (exists_cnt >= 0 && error_cnt == 0) {
			result = OVAL_RESULT_TRUE;
		} else if (exists_cnt >= 1 && error_cnt >= 1) {
			result = OVAL_RESULT_TRUE;
		} else if (exists_cnt == 0 && error_cnt >= 1) {
			result = OVAL_RESULT_ERROR;
		}
		break;
	case OVAL_AT_LEAST_ONE_EXISTS:
		if (exists_cnt >= 1 && error_cnt >= 0) {
			result = OVAL_RESULT_TRUE;
		} else if (exists_cnt == 0 && error_cnt == 0) {
			result = OVAL_RESULT_FALSE;
		} else if (exists_cnt == 0 && error_cnt >= 1) {
			result = OVAL_RESULT_ERROR;
		}
		break;
	case OVAL_NONE_EXIST:
		if (exists_cnt == 0 && error_cnt == 0) {
			result = OVAL_RESULT_TRUE;
		} else if (exists_cnt >= 1 && error_cnt == 0) {
			result = OVAL_RESULT_FALSE;
		} else if (exists_cnt == 0 && error_cnt >= 1) {
			result = OVAL_RESULT_ERROR;
		}
		break;
	case OVAL_ONLY_ONE_EXISTS:
		if (exists_cnt == 1 && error_cnt == 0) {
			result = OVAL_RESULT_TRUE;
		} else if (exists_cnt >= 2 && error_cnt >= 0) {
			result = OVAL_RESULT_FALSE;
		} else if (exists_cnt == 0 && error_cnt == 0) {
			result = OVAL_RESULT_FALSE;
		} else if (exists_cnt == 0 && error_cnt >= 1) {
			result = OVAL_RESULT_ERROR;
		} else if (exists_cnt == 1 && error_cnt >= 1) {
			result = OVAL_RESULT_ERROR;
		}
		break;
	default:
		oscap_seterr(OSCAP_EFAMILY_OSCAP, "Invalid check_existence value: %d.", check_existence);
                result = OVAL_RESULT_ERROR;
		break;
	}

	return result;
}

static oval_result_t
_oval_result_test_evaluate_items(struct oval_test *test, struct oval_syschar *syschar_object, void **args)
{
	struct oval_sysitem_iterator *collected_items_itr;
	oval_result_t result;
	int exists_cnt, error_cnt;
	bool hasstate;
	const char *test_id, *object_id, *flag_text;
	oval_check_t test_check;
	oval_existence_t test_check_existence;
	struct oval_state_iterator *ste_itr;
	oval_syschar_collection_flag_t flag;
	struct oval_object *object;

	exists_cnt = error_cnt = 0;
	test_id = oval_test_get_id(test);
	collected_items_itr = oval_syschar_get_sysitem(syschar_object);
	while (oval_sysitem_iterator_has_more(collected_items_itr)) {
		struct oval_sysitem *item;
		char *item_id;
		oval_syschar_status_t item_status;
		struct oval_result_item *ritem;

		item = oval_sysitem_iterator_next(collected_items_itr);
		if (item == NULL) {
			oscap_seterr(OSCAP_EFAMILY_OVAL, "Iterator returned null.");
			oval_sysitem_iterator_free(collected_items_itr);
			return OVAL_RESULT_ERROR;
		}

		item_status = oval_sysitem_get_status(item);
		if (item_status == SYSCHAR_STATUS_EXISTS)
			exists_cnt++;
		if (item_status == SYSCHAR_STATUS_ERROR)
			error_cnt++;

		item_id = oval_sysitem_get_id(item);
		ritem = oval_result_item_new(SYSTEM, item_id);
		oval_result_item_set_result(ritem, OVAL_RESULT_NOT_EVALUATED);
		_oval_test_item_consumer(ritem, args);
	}
	oval_sysitem_iterator_free(collected_items_itr);

	test_check = oval_test_get_check(test);
	test_check_existence = oval_test_get_existence(test);
	ste_itr = oval_test_get_states(test);
	hasstate = oval_state_iterator_has_more(ste_itr);
	oval_state_iterator_free(ste_itr);
	object = oval_syschar_get_object(syschar_object);
	object_id = object ? oval_object_get_id(object) : "<UNKNOWN>";

	switch (test_check_existence) {
	case OVAL_ALL_EXIST:
		dI("Test '%s' requires that every object defined by '%s' exists on the system.", test_id, object_id);
		break;
	case OVAL_ANY_EXIST:
		dI("Test '%s' requires that zero or more objects defined by '%s' exist on the system.", test_id, object_id);
		break;
	case OVAL_AT_LEAST_ONE_EXISTS:
		dI("Test '%s' requires that at least one object defined by '%s' exists on the system.", test_id, object_id);
		break;
	case OVAL_NONE_EXIST:
		dI("Test '%s' requires that none of the objects defined by '%s' exist on the system.", test_id, object_id);
		break;
	case OVAL_ONLY_ONE_EXISTS:
		dI("Test '%s' requires that only one object defined by '%s' exists on the system.", test_id, object_id);
		break;
	default:
		oscap_seterr(OSCAP_EFAMILY_OVAL, "Check_existence parameter of test '%s' is unknown. This may indicate a bug in OpenSCAP.", test_id);
	}

	dI("%d objects defined by '%s' exist on the system.", exists_cnt, object_id);
	if (!hasstate) {
		dI("Test '%s' does not contain any state to compare object with.", test_id);
	}
	flag = oval_syschar_get_flag(syschar_object);
	flag_text = oval_syschar_collection_flag_get_text(flag);

	switch (flag) {
	case SYSCHAR_FLAG_ERROR:
		dI("An error occurred while collecting items matching object '%s'. (flag=%s)", object_id, flag_text);
		if (test_check_existence == OVAL_ANY_EXIST
		    && !hasstate) {
			result = OVAL_RESULT_TRUE;
		} else {
			result = OVAL_RESULT_ERROR;
		}
		break;
	case SYSCHAR_FLAG_NOT_COLLECTED:
		dI("No attempt was made to collect items matching object '%s'. (flag=%s)", object_id, flag_text);
		if (test_check_existence == OVAL_ANY_EXIST
		    && !hasstate) {
			result = OVAL_RESULT_TRUE;
		} else {
			result = OVAL_RESULT_UNKNOWN;
		}
		break;
	case SYSCHAR_FLAG_NOT_APPLICABLE:
		dI("Object '%s' is not applicable to the system. (flag=%s)", object_id, flag_text);
		if (test_check_existence == OVAL_ANY_EXIST
		    && !hasstate) {
			result = OVAL_RESULT_TRUE;
		} else {
			result = OVAL_RESULT_NOT_APPLICABLE;
		}
		break;
	case SYSCHAR_FLAG_DOES_NOT_EXIST:
		dI("No item matching object '%s' was found on the system. (flag=%s)", object_id, flag_text);
		if (test_check_existence == OVAL_NONE_EXIST
		    || test_check_existence == OVAL_ANY_EXIST) {
			result = OVAL_RESULT_TRUE;
		} else {
			result = OVAL_RESULT_FALSE;
		}
		break;
	case SYSCHAR_FLAG_COMPLETE:
		dI("All items matching object '%s' were collected. (flag=%s)", object_id, flag_text);
		result = eval_check_existence(test_check_existence, exists_cnt, error_cnt);
		if (result == OVAL_RESULT_TRUE
		    && hasstate) {
			result = eval_check_state(test, args);
		}
		break;
	case SYSCHAR_FLAG_INCOMPLETE:
		dI("Only some of items matching object '%s' have been collected from the system. It is unknown if other matching items also exist. (flag=%s)", object_id, flag_text);
		if (test_check_existence == OVAL_ANY_EXIST) {
			result = OVAL_RESULT_TRUE;
		} else if (test_check_existence == OVAL_AT_LEAST_ONE_EXISTS
			   && exists_cnt > 0) {
			result = OVAL_RESULT_TRUE;
		} else if (test_check_existence == OVAL_NONE_EXIST
			   && exists_cnt > 0) {
			result = OVAL_RESULT_FALSE;
		} else if (test_check_existence == OVAL_ONLY_ONE_EXISTS
			   && exists_cnt > 1) {
			result = OVAL_RESULT_FALSE;
		} else {
			result = OVAL_RESULT_UNKNOWN;
		}

		if (result == OVAL_RESULT_TRUE
		    && hasstate) {
			result = eval_check_state(test, args);
			if (result == OVAL_RESULT_TRUE) {
				if (test_check != OVAL_CHECK_AT_LEAST_ONE) {
					result = OVAL_RESULT_UNKNOWN;
				}
			} else if (result != OVAL_RESULT_FALSE) {
				result = OVAL_RESULT_UNKNOWN;
			}
		}
		break;
	default: {
		oscap_seterr(OSCAP_EFAMILY_OVAL, "Item corresponding to object '%s' from test '%s' has an unknown flag. This may indicate a bug in OpenSCAP.",
				object_id, test_id);
		return OVAL_RESULT_ERROR;
		}
	}

	return result;
}

/* this function will gather all the necessary ingredients and call 'evaluate_items' when it finds them */
static oval_result_t _oval_result_test_result(struct oval_result_test *rtest, void **args)
{
	__attribute__nonnull__(rtest);

	/* is the test already evaluated? */
	if (rtest->result != OVAL_RESULT_NOT_EVALUATED) {
		dI("Found result from previous evaluation: %d, returning without further processing.", rtest->result);
		return (rtest->result);
	}

#if defined(OVAL_PROBES_ENABLED)
	/* get syschar of rtest */
	struct oval_test *test = oval_result_test_get_test(rtest);
	struct oval_object * object = oval_test_get_object(test);
	char * object_id = oval_object_get_id(object);

	struct oval_result_system *sys = oval_result_test_get_system(rtest);
	struct oval_results_model *results_model = oval_result_system_get_results_model(sys);
	struct oval_probe_session *probe_session = oval_results_model_get_probe_session(results_model);
	if (probe_session != NULL) {
		/* probe test */
		int ret = oval_probe_query_test(probe_session, test);
		if (ret != 0) {
			return ret;
		}
	}

	struct oval_syschar_model *syschar_model = oval_result_system_get_syschar_model(sys);

	struct oval_syschar * syschar = oval_syschar_model_get_syschar(syschar_model, object_id);
	if (syschar == NULL) {
		dW("No syschar for object: %s", object_id);
		return OVAL_RESULT_UNKNOWN;
	}

	/* evaluate items */
	oval_result_t result = _oval_result_test_evaluate_items(test, syschar, args);

	return result;
#else
	return OVAL_RESULT_UNKNOWN;
#endif
}

static void _oval_result_test_initialize_bindings(struct oval_result_test *rslt_test)
{
	__attribute__nonnull__(rslt_test);

	struct oval_test *oval_test = oval_result_test_get_test(rslt_test);
	struct oval_string_map *vm;
	struct oval_state_iterator *ste_itr;
	struct oval_iterator *var_itr;

	vm = oval_string_map_new();

	/* Gather bindings pertaining to the referenced states */
	/* TODO: cache bindings collected for each state */
	ste_itr = oval_test_get_states(oval_test);
	while (oval_state_iterator_has_more(ste_itr)) {
		struct oval_state *ste;

		ste = oval_state_iterator_next(ste_itr);
		oval_ste_collect_var_refs(ste, vm);
	}
	oval_state_iterator_free(ste_itr);

	var_itr = oval_string_map_values(vm);
	while (oval_collection_iterator_has_more(var_itr)) {
		struct oval_variable *var;
		struct oval_value_iterator *val_itr;
		struct oval_variable_binding *binding;

		var = oval_collection_iterator_next(var_itr);
		binding = oval_variable_binding_new(var, NULL);

		val_itr = oval_variable_get_values(var);
		while (oval_value_iterator_has_more(val_itr)) {
			struct oval_value *val;
			char *txt;

			val = oval_value_iterator_next(val_itr);
			txt = oval_value_get_text(val);
			txt = oscap_strdup(txt);
			oval_variable_binding_add_value(binding, txt);
		}
		oval_value_iterator_free(val_itr);
		oval_result_test_add_binding(rslt_test, binding);
	}
	oval_collection_iterator_free(var_itr);

	/* Gather bindings pertaining to the collected object */
	struct oval_object *oval_object = oval_test_get_object(oval_test);
	if (oval_object) {
		char *object_id = oval_object_get_id(oval_object);
		struct oval_result_system *sys = oval_result_test_get_system(rslt_test);
		struct oval_syschar_model *syschar_model = oval_result_system_get_syschar_model(sys);
		struct oval_syschar *syschar = oval_syschar_model_get_syschar(syschar_model, object_id);
		/* no syschar if system characteristics was a subset of definitions */
		if(syschar) {
			struct oval_variable_binding_iterator *bindings = oval_syschar_get_variable_bindings(syschar);
			while (oval_variable_binding_iterator_has_more(bindings)) {
				struct oval_variable *var;
				char *var_id;
				struct oval_variable_binding *binding = oval_variable_binding_iterator_next(bindings);

				var = oval_variable_binding_get_variable(binding);
				var_id = oval_variable_get_id(var);
				/* Don't add bindings that were already
				 * collected from states. Assumtion is made
				 * that object's own bindings don't contain
				 * duplicates.
				 */
				if (oval_string_map_get_value(vm, var_id) == NULL) {
					struct oval_definition_model *definition_model = oval_syschar_model_get_definition_model(syschar_model);
					struct oval_variable_binding *binding_copy = oval_variable_binding_clone(binding, definition_model);
					oval_result_test_add_binding(rslt_test, binding_copy);
				}
			}
			oval_variable_binding_iterator_free(bindings);
		}
	}

	oval_string_map_free(vm, NULL);

	rslt_test->bindings_initialized = true;
}

oval_result_t oval_result_test_eval(struct oval_result_test *rtest)
{
	__attribute__nonnull__(rtest);

	struct oval_test *test = oval_result_test_get_test(rtest);
	const char *type = oval_subtype_get_text(oval_test_get_subtype(test));
	const char *test_id = oval_test_get_id(test);
	const char *comment = oval_test_get_comment(test);
	dI("Evaluating %s test '%s': %s.", type, test_id, comment);

	if (rtest->result == OVAL_RESULT_NOT_EVALUATED) {
		if (oval_test_get_subtype(oval_result_test_get_test(rtest)) != OVAL_INDEPENDENT_UNKNOWN) {
			struct oval_string_map *tmp_map = oval_string_map_new();
			void *args[] = { rtest->system, rtest, tmp_map };
			dIndent(1);
			rtest->result = _oval_result_test_result(rtest, args);
			dIndent(-1);
			oval_string_map_free(tmp_map, NULL);

			if (!rtest->bindings_initialized) {
				_oval_result_test_initialize_bindings(rtest);
			}
		}
		else
			rtest->result = OVAL_RESULT_UNKNOWN;
	}

	dI("Test '%s' evaluated as %s.", test_id, oval_result_get_text(rtest->result));

	return rtest->result;
}

oval_result_t oval_result_test_get_result(struct oval_result_test * rtest)
{
	__attribute__nonnull__(rtest);

	return rtest->result;
}

int oval_result_test_get_instance(struct oval_result_test *rtest)
{
	__attribute__nonnull__(rtest);

	return rtest->instance;
}

struct oval_message_iterator *oval_result_test_get_messages(struct oval_result_test *rtest)
{
	__attribute__nonnull__(rtest);

	return (struct oval_message_iterator *) oval_collection_iterator(rtest->messages);
}

struct oval_result_item_iterator *oval_result_test_get_items(struct oval_result_test *rtest)
{
	__attribute__nonnull__(rtest);

	return (struct oval_result_item_iterator *)
	    oval_collection_iterator(rtest->items);
}

struct oval_variable_binding_iterator *oval_result_test_get_bindings(struct oval_result_test
								     *rtest)
{
	__attribute__nonnull__(rtest);

	return (struct oval_variable_binding_iterator *)
	    oval_collection_iterator(rtest->bindings);
}

void oval_result_test_set_result(struct oval_result_test *test, oval_result_t result)
{
	__attribute__nonnull__(test);
	test->result = result;
}

void oval_result_test_set_instance(struct oval_result_test *test, int instance)
{
	__attribute__nonnull__(test);
	test->instance = instance;
}

void oval_result_test_add_message(struct oval_result_test *test, struct oval_message *message)
{
	__attribute__nonnull__(test);
	oval_collection_add(test->messages, message);
}

void oval_result_test_add_item(struct oval_result_test *test, struct oval_result_item *item)
{
	__attribute__nonnull__(test);
	oval_collection_add(test->items, item);
}

void oval_result_test_add_binding(struct oval_result_test *test, struct oval_variable_binding *binding)
{
	__attribute__nonnull__(test);
	oval_collection_add(test->bindings, binding);
}

//void(*oscap_consumer_func)(void*, void*);
static void _oval_test_message_consumer(struct oval_message *message, struct oval_result_test *test) {
	oval_result_test_add_message(test, message);
}

static int _oval_result_test_binding_parse(xmlTextReaderPtr reader, struct oval_parser_context *context, void **args) {
	int return_code = 0;

	xmlChar *variable_id = xmlTextReaderGetAttribute(reader, BAD_CAST "variable_id");

	struct oval_syschar_model *syschar_model = oval_result_system_get_syschar_model(SYSTEM);
	struct oval_definition_model *definition_model = oval_syschar_model_get_definition_model(syschar_model);

	struct oval_variable *variable = oval_definition_model_get_new_variable
	    (definition_model, (char *)variable_id, OVAL_VARIABLE_UNKNOWN);

	xmlChar *value = xmlTextReaderValue(reader);

	struct oval_variable_binding *binding = oval_variable_binding_new(variable, oscap_strdup((char *) value));
	oval_result_test_add_binding(TEST, binding);

	xmlFree(value);
	xmlFree(variable_id);

	return return_code;
}

static int _oval_result_test_parse(xmlTextReaderPtr reader, struct oval_parser_context *context, void **args) {
	int return_code = 0;
	xmlChar *localName = xmlTextReaderLocalName(reader);

	if (strcmp((const char *)localName, "message") == 0) {
		return_code = oval_message_parse_tag(reader, context, (oscap_consumer_func) _oval_test_message_consumer, TEST);
	} else if (strcmp((const char *)localName, "tested_item") == 0) {
		return_code = oval_result_item_parse_tag(reader, context, SYSTEM, (oscap_consumer_func) _oval_test_item_consumer, args);
	} else if (strcmp((const char *)localName, "tested_variable") == 0) {
		return_code = _oval_result_test_binding_parse(reader, context, args);
	} else {
		dW("Unhandled tag: <%s>.", localName);
		oval_parser_skip_tag(reader, context);
	}

	free(localName);

	return return_code;
}

int oval_result_test_parse_tag(xmlTextReaderPtr reader, struct oval_parser_context *context, void *usr) {

	struct oval_result_system *sys = (struct oval_result_system *) usr;
	int return_code = 0;
	struct oval_definition_model *dmod;
	struct oval_test *dtst;
	struct oval_result_test *test;
	xmlChar *test_id = xmlTextReaderGetAttribute(reader, BAD_CAST "test_id");

	dmod = context->definition_model;
	dtst = oval_definition_model_get_new_test(dmod, (char *) test_id);
	oval_result_t result = oval_result_parse(reader, "result", 0);
	int variable_instance = oval_parser_int_attribute(reader, "variable_instance", 1);

	test = oval_result_system_get_new_test(sys, dtst, variable_instance);
	if (test == NULL)
		return -1;
	oval_result_test_set_result(test, result);
	oval_result_test_set_instance(test, variable_instance);

	struct oval_test *ovaltst = oval_result_test_get_test(test);

	oval_existence_t check_existence = oval_existence_parse(reader, "check_existence", OVAL_AT_LEAST_ONE_EXISTS);
	oval_existence_t tst_check_existence = oval_test_get_existence(ovaltst);
	if (tst_check_existence == OVAL_EXISTENCE_UNKNOWN) {
		oval_test_set_existence(ovaltst, check_existence);
	} else if (tst_check_existence != check_existence) {
		dW("@check_existence does not match, test_id: %s.", test_id);
	}

	oval_check_t check = oval_check_parse(reader, "check", OVAL_CHECK_UNKNOWN);
	oval_check_t tst_check = oval_test_get_check(ovaltst);
	if (tst_check == OVAL_CHECK_UNKNOWN) {
		oval_test_set_check(ovaltst, check);
	} else if (tst_check != check) {
		dW("@check does not match, test_id: %s.", test_id);
	}

	int version = oval_parser_int_attribute(reader, "version", 0);
	int tst_version = oval_test_get_version(ovaltst);
	if (tst_version == 0) {
		oval_test_set_version(ovaltst, version);
	} else if (tst_version != version) {
		dW("@version does not match, test_id: %s.", test_id);
	}

	struct oval_string_map *itemmap = oval_string_map_new();
	void *args[] = { sys, test, itemmap };
	return_code = oval_parser_parse_tag(reader, context, (oval_xml_tag_parser) _oval_result_test_parse, args);
	oval_string_map_free(itemmap, NULL);
	test->bindings_initialized = true;

	free(test_id);
	return return_code;
}

static void _oval_result_binding_to_dom(struct oval_variable_binding *binding, xmlDocPtr doc, xmlNode *parent) {
	xmlNs *ns_results = xmlSearchNsByHref(doc, parent, OVAL_RESULTS_NAMESPACE);
	struct oval_variable *oval_variable = oval_variable_binding_get_variable(binding);
	char *variable_id = oval_variable_get_id(oval_variable);
	struct oval_string_iterator *str_itr;

	str_itr = oval_variable_binding_get_values(binding);
	while (oval_string_iterator_has_more(str_itr)) {
		char *value;
		xmlNode *binding_node;

		value = oval_string_iterator_next(str_itr);
		binding_node = xmlNewTextChild(parent, ns_results, BAD_CAST "tested_variable", BAD_CAST value);
		xmlNewProp(binding_node, BAD_CAST "variable_id", BAD_CAST variable_id);
	}
	oval_string_iterator_free(str_itr);
}

xmlNode *oval_result_test_to_dom(struct oval_result_test *rslt_test, xmlDocPtr doc, xmlNode * parent) {
	__attribute__nonnull__(rslt_test);

	xmlNs *ns_results = xmlSearchNsByHref(doc, parent, OVAL_RESULTS_NAMESPACE);
	xmlNode *test_node = xmlNewTextChild(parent, ns_results, BAD_CAST "test", NULL);

	struct oval_test *oval_test = oval_result_test_get_test(rslt_test);
	char *test_id = oval_test_get_id(oval_test);
	xmlNewProp(test_node, BAD_CAST "test_id", BAD_CAST test_id);

	char version[10];
	*version = '\0';
	snprintf(version, sizeof(version), "%d", oval_test_get_version(oval_test));
	xmlNewProp(test_node, BAD_CAST "version", BAD_CAST version);

	oval_existence_t existence = oval_test_get_existence(oval_test);
	if (existence != OVAL_AT_LEAST_ONE_EXISTS) {
		xmlNewProp(test_node, BAD_CAST "check_existence", BAD_CAST oval_existence_get_text(existence));
	}

	oval_check_t check = oval_test_get_check(oval_test);
	xmlNewProp(test_node, BAD_CAST "check", BAD_CAST oval_check_get_text(check));

	int instance_val = oval_result_test_get_instance(rslt_test);
	if (instance_val > 1) {
		char instance[11];
		*instance = '\0';
		snprintf(instance, sizeof(instance), "%d", instance_val);
		xmlNewProp(test_node, BAD_CAST "variable_instance", BAD_CAST instance);
	}

	oval_result_t result = oval_result_test_get_result(rslt_test);
	xmlNewProp(test_node, BAD_CAST "result", BAD_CAST oval_result_get_text(result));

	/* does not make sense to report these when test(definition) is not evaluated */
	if( result != OVAL_RESULT_NOT_EVALUATED) {
		struct oval_result_item_iterator *items = oval_result_test_get_items(rslt_test);
		while (oval_result_item_iterator_has_more(items)) {
			struct oval_result_item *item = oval_result_item_iterator_next(items);
			oval_result_item_to_dom(item, doc, test_node);
		}
		oval_result_item_iterator_free(items);

		struct oval_variable_binding_iterator *bindings = oval_result_test_get_bindings(rslt_test);
		while (oval_variable_binding_iterator_has_more(bindings)) {
			struct oval_variable_binding *binding = oval_variable_binding_iterator_next(bindings);
			_oval_result_binding_to_dom(binding, doc, test_node);
		}
		oval_variable_binding_iterator_free(bindings);
	}

	return test_node;
}

const char *oval_result_test_get_id(const struct oval_result_test *rslt_test)
{
	__attribute__nonnull__(rslt_test);
	struct oval_test *test = oval_result_test_get_test((struct oval_result_test *) rslt_test);
	return (test == NULL) ? NULL : oval_test_get_id(test);
}
