/**
 * @file oval_resModel.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>
 */

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

#include <string.h>
#include <time.h>

#include "oval_definitions_impl.h"
#include "oval_agent_api_impl.h"
#include "oval_parser_impl.h"
#include "adt/oval_string_map_impl.h"
#include "oval_system_characteristics_impl.h"
#include "results/oval_results_impl.h"
#if defined(OVAL_PROBES_ENABLED)
# include "oval_probe_impl.h"
#endif
#include "oval_directives_impl.h"

#include "common/util.h"
#include "common/debug_priv.h"
#include "common/_error.h"
#include "common/elements.h"
#include "oscap_source.h"
#include "source/oscap_source_priv.h"

struct oval_results_model {
	struct oval_generator *generator;
	struct oval_directives_model *directives_model;
	struct oval_definition_model *definition_model;
	struct oval_collection *systems;
#if defined(OVAL_PROBES_ENABLED)
	struct oval_probe_session *probe_session;
#endif
	bool   export_sys_chars;
};

struct oval_results_model *oval_results_model_new(struct oval_definition_model *definition_model,
						  struct oval_syschar_model **syschar_models)
{
#if defined(OVAL_PROBES_ENABLED)
	return oval_results_model_new_with_probe_session(definition_model, syschar_models, NULL);
}

struct oval_results_model *oval_results_model_new_with_probe_session(struct oval_definition_model *definition_model,
						  struct oval_syschar_model **syschar_models,
						  struct oval_probe_session *probe_session)
{
#endif
	struct oval_results_model *model = (struct oval_results_model *) malloc(sizeof(struct oval_results_model));
	if (model == NULL)
		return NULL;

	struct oval_generator *generator = oval_definition_model_get_generator(definition_model);
	model->generator = oval_generator_clone(generator);
	oval_generator_update_timestamp(model->generator);

	model->systems = oval_collection_new();
	model->definition_model = definition_model;
	if (syschar_models) {
		struct oval_syschar_model *syschar_model;
		for (syschar_model = *syschar_models; syschar_model; syschar_model = *(++syschar_models)) {
			oval_result_system_new(model, syschar_model);
		}
	}
	model->directives_model = oval_directives_model_new();
#if defined(OVAL_PROBES_ENABLED)
	model->probe_session = probe_session;
#endif
	model->export_sys_chars = true;
	return model;
}

struct oval_results_model *oval_results_model_clone(struct oval_results_model *old_resmodel)
{
	struct oval_definition_model *old_defmodel = oval_results_model_get_definition_model(old_resmodel);
	struct oval_results_model *new_resmodel = oval_results_model_new(old_defmodel, NULL);
	struct oval_result_system_iterator *old_systems = oval_results_model_get_systems(old_resmodel);

	while (oval_result_system_iterator_has_more(old_systems)) {
		struct oval_result_system *old_system = oval_result_system_iterator_next(old_systems);
		oval_result_system_clone(new_resmodel, old_system);
	}
	oval_result_system_iterator_free(old_systems);
	/* ToDo: Directives Model clone */

	return new_resmodel;
}

void oval_results_model_set_export_system_characteristics(struct oval_results_model *model, bool export)
{
	model->export_sys_chars = export;
}

bool oval_results_model_get_export_system_characteristics(struct oval_results_model *model)
{
	return model->export_sys_chars;
}

void oval_results_model_free(struct oval_results_model *model)
{
	__attribute__nonnull__(model);

	oval_collection_free_items(model->systems, (oscap_destruct_func) oval_result_system_free);
	model->systems = NULL;
	model->definition_model = NULL;

	oval_generator_free(model->generator);
	model->generator=NULL;

	oval_directives_model_free(model->directives_model);
	model->directives_model=NULL;

	free(model);
}

struct oval_generator *oval_results_model_get_generator(struct oval_results_model *model)
{
	return model->generator;
}

void oval_results_model_set_generator(struct oval_results_model *model, struct oval_generator *generator)
{
	oval_generator_free(model->generator);
	model->generator = generator;
}

struct oval_directives_model *oval_results_model_get_directives_model(struct oval_results_model *model) {
	__attribute__nonnull__(model);

	return model->directives_model;
}

struct oval_definition_model *oval_results_model_get_definition_model(struct oval_results_model *model) {
	__attribute__nonnull__(model);

	return model->definition_model;
}

struct oval_result_system_iterator *oval_results_model_get_systems(struct oval_results_model *model)
{
	__attribute__nonnull__(model);

	return (struct oval_result_system_iterator *)
	    oval_collection_iterator(model->systems);
}

#if defined(OVAL_PROBES_ENABLED)
struct oval_probe_session *oval_results_model_get_probe_session(struct oval_results_model *model)
{
	__attribute__nonnull__(model);
	return model->probe_session;
}
#endif

void oval_results_model_add_system(struct oval_results_model *model, struct oval_result_system *sys)
{
	__attribute__nonnull__(model);
	if (sys)
		oval_collection_add(model->systems, sys);
}

int oval_results_model_import_source(struct oval_results_model *model, struct oscap_source *source)
{
	__attribute__nonnull__(model);
	__attribute__nonnull__(source);

	int ret = 0;
	char *tagname = NULL;
	char *namespace = NULL;

	/* setup context */
	struct oval_parser_context context;
	context.reader = oscap_source_get_xmlTextReader(source);
	if (context.reader == NULL) {
		return -1;
	}
	context.results_model = model;
	context.definition_model = oval_results_model_get_definition_model(model);
	context.user_data = NULL;
	oscap_setxmlerr(xmlGetLastError());
	/* jump into document */
	xmlTextReaderRead(context.reader);
	/* make sure these are results */
	tagname = (char *)xmlTextReaderLocalName(context.reader);
	namespace = (char *)xmlTextReaderNamespaceUri(context.reader);
	int is_ovalres = strcmp((const char *)OVAL_RESULTS_NAMESPACE, namespace) == 0;
	/* star parsing */
	if (is_ovalres && (strcmp(tagname, OVAL_ROOT_ELM_RESULTS) == 0)) {
		ret = oval_results_model_parse(context.reader, &context);
	} else {
                oscap_seterr(OSCAP_EFAMILY_OSCAP, "Missing \"oval_results\" element");
		ret = -1;
	}

        free(tagname);
        free(namespace);
	xmlFreeTextReader(context.reader);
	return ret;
}

int oval_results_model_eval(struct oval_results_model *res_model)
{
	struct oval_result_system_iterator *systems_itr;

	systems_itr = oval_results_model_get_systems(res_model);

	while (oval_result_system_iterator_has_more(systems_itr)) {
		struct oval_result_system *sys;

		sys = oval_result_system_iterator_next(systems_itr);
		if( oval_result_system_eval(sys) != 0 ) { 	/* evaluate each result_system */
			oval_result_system_iterator_free(systems_itr);
			return -1;
		}
	}
	oval_result_system_iterator_free(systems_itr);
	return 0;
}

static xmlNode *oval_results_to_dom(struct oval_results_model *results_model,
				    struct oval_directives_model *directives_model, 
				    xmlDocPtr doc, xmlNode * parent)
{
	xmlNode *root_node;
	struct oval_result_directives * dirs;
	struct oval_directives_model * dirs_model;

	if (parent) {
		root_node = xmlNewTextChild(parent, NULL, BAD_CAST OVAL_ROOT_ELM_RESULTS, NULL);
	} else {
		root_node = xmlNewNode(NULL, BAD_CAST OVAL_ROOT_ELM_RESULTS);
		xmlDocSetRootElement(doc, root_node);
	}
	xmlNewNsProp(root_node, lookup_xsi_ns(doc), BAD_CAST "schemaLocation", BAD_CAST OVAL_RES_SCHEMA_LOCATION);

	xmlNs *ns_common = xmlNewNs(root_node, OVAL_COMMON_NAMESPACE, BAD_CAST "oval");
	xmlNs *ns_results = xmlNewNs(root_node, OVAL_RESULTS_NAMESPACE, NULL);

	xmlSetNs(root_node, ns_common);
	xmlSetNs(root_node, ns_results);

	/* Report generator */
	oval_generator_to_dom(results_model->generator, doc, root_node);

	/* Report default directives and class directives from internal or external
	 * directives model(if provided) */
	dirs_model = (directives_model) ? directives_model : results_model->directives_model;
	oval_directives_model_to_dom(dirs_model, doc, root_node);

	dirs = oval_directives_model_get_defdirs(dirs_model);

	/* Report definitions */
	if(oval_result_directives_get_included(dirs)) {
		struct oval_definition_model *definition_model = oval_results_model_get_definition_model(results_model);
		oval_definition_model_to_dom(definition_model, doc, root_node);
	}

	xmlNode *results_node = xmlNewTextChild(root_node, ns_results, BAD_CAST "results", NULL);
	struct oval_result_system_iterator *systems = oval_results_model_get_systems(results_model);
	while (oval_result_system_iterator_has_more(systems)) {
		struct oval_result_system *sys = oval_result_system_iterator_next(systems);
		oval_result_system_to_dom(sys, results_model, dirs_model, doc, results_node);
	}
	oval_result_system_iterator_free(systems);

	return root_node;
}

struct oscap_source *oval_results_model_export_source(struct oval_results_model *results_model, struct oval_directives_model *directives_model, const char *name)
{
	__attribute__nonnull__(results_model);

	xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
	if (doc == NULL) {
		oscap_setxmlerr(xmlGetLastError());
		return NULL;
	}

	oval_results_to_dom(results_model, directives_model, doc, NULL);
	return oscap_source_new_from_xmlDoc(doc, name);
}

int oval_results_model_export(struct oval_results_model *results_model,
			      struct oval_directives_model *directives_model,
			      const char *file)
{
	struct oscap_source *source = oval_results_model_export_source(results_model, directives_model, file);
	if (source == NULL) {
		return -1;
	}
	int ret = oscap_source_save_as(source, NULL);
	oscap_source_free(source);
	return ret;
}

int oval_results_model_parse(xmlTextReaderPtr reader, struct oval_parser_context *context) {
        int depth = xmlTextReaderDepth(reader);
        int ret = 0;

        xmlTextReaderRead(reader);
        while ((xmlTextReaderDepth(reader) > depth) && (ret != -1 )) {
                if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
                        char *tagname = (char *)xmlTextReaderLocalName(reader);
                        char *namespace = (char *)xmlTextReaderNamespaceUri(reader);

                        int is_ovalres = strcmp((const char *)OVAL_RESULTS_NAMESPACE, namespace) == 0;
                        int is_ovaldef = (is_ovalres) ? false : (strcmp((const char *)OVAL_DEFINITIONS_NAMESPACE, namespace) == 0);
                        if (is_ovalres && (strcmp(tagname, "generator") == 0)) {
                                struct oval_generator *gen;
                                gen = oval_results_model_get_generator(context->results_model);
                                ret = oval_parser_parse_tag(reader, context, &oval_generator_parse_tag, gen);
                        } else if (is_ovalres && (strcmp(tagname, "directives") == 0)) {
				struct oval_result_directives *def_dirs;
				bool val;
				def_dirs = oval_directives_model_get_defdirs(context->results_model->directives_model);
				val = oval_parser_boolean_attribute(reader, "include_source_definitions", 1);
				oval_result_directives_set_included(def_dirs, val);
				ret = oval_parser_parse_tag(reader, context, &oval_result_directives_parse_tag, def_dirs);
                        } else if (is_ovalres && (strcmp(tagname, "class_directives") == 0)) {
                                struct oval_result_directives *class_dir;
                                oval_definition_class_t class_type;
                                char *class_str = (char *)xmlTextReaderGetAttribute(reader, BAD_CAST "class");
                                class_type = oval_definition_class_enum(class_str);
                                class_dir = oval_directives_model_get_new_classdir(context->results_model->directives_model, class_type);
                                ret = oval_parser_parse_tag(reader, context, &oval_result_directives_parse_tag, class_dir);
                                free(class_str);
                        } else if (is_ovaldef && (strcmp(tagname, OVAL_ROOT_ELM_DEFINITIONS) == 0)) {
                                ret = oval_definition_model_parse(reader, context);
                        } else if (is_ovalres && (strcmp(tagname, "results") == 0)) {
                                ret = oval_parser_parse_tag(reader, context, &oval_result_system_parse_tag , NULL);
                        } else {
                                dW("Unprocessed tag: <%s:%s>.", namespace, tagname);
                                oval_parser_skip_tag(reader, context);
                        }

                        free(tagname);
                        free(namespace);
                } else {
                        if (xmlTextReaderRead(reader) != 1) {
                                ret = -1;
                                break;
                        }
                }
        }

        return ret;
}


