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

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

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "oval_definitions_impl.h"
#include "adt/oval_collection_impl.h"
#include "oval_parser_impl.h"
#include "adt/oval_string_map_impl.h"

#include "common/util.h"
#include "common/debug_priv.h"
#include "common/elements.h"
#include "common/_error.h"

/***************************************************************************/
/* Variable definitions
 * */

typedef struct oval_affected {
	struct oval_definition_model *model;
	oval_affected_family_t family;
	struct oval_collection *platforms;
	struct oval_collection *products;
} oval_affected_t;

static const struct oscap_string_map OVAL_ODAFAMILY_MAP[] = {
	{OVAL_AFCFML_CATOS, "catos"},
	{OVAL_AFCFML_IOS, "ios"},
	{OVAL_AFCFML_MACOS, "macos"},
	{OVAL_AFCFML_PIXOS, "pixos"},
	{OVAL_AFCFML_UNDEFINED, "undefined"},
	{OVAL_AFCFML_UNIX, "unix"},
	{OVAL_AFCFML_WINDOWS, "windows"},
	// the "unknown" is not a valid OVAL family value, we use it internally
	// when the supplied value is invalid
	{OVAL_AFCFML_UNKNOWN, NULL}
};

/* End of variable definitions
 * */
/***************************************************************************/

bool oval_affected_iterator_has_more(struct oval_affected_iterator *oc_affected)
{
	return oval_collection_iterator_has_more((struct oval_iterator *)
						 oc_affected);
}

struct oval_affected *oval_affected_iterator_next(struct oval_affected_iterator
						  *oc_affected)
{
	return (struct oval_affected *)
	    oval_collection_iterator_next((struct oval_iterator *)oc_affected);
}

void oval_affected_iterator_free(struct oval_affected_iterator
				 *oc_affected)
{
	oval_collection_iterator_free((struct oval_iterator *)oc_affected);
}

oval_affected_family_t oval_affected_get_family(struct oval_affected *affected)
{
	__attribute__nonnull__(affected);

	return ((struct oval_affected *)affected)->family;
}

struct oval_string_iterator *oval_affected_get_platforms(struct oval_affected *affected)
{
	__attribute__nonnull__(affected);

	return (struct oval_string_iterator *)
	    oval_collection_iterator(affected->platforms);
}

struct oval_string_iterator *oval_affected_get_products(struct oval_affected *affected)
{
	__attribute__nonnull__(affected);

	return (struct oval_string_iterator *)
	    oval_collection_iterator(affected->products);
}

struct oval_affected *oval_affected_new(struct oval_definition_model *model)
{
	struct oval_affected *affected = (struct oval_affected *)malloc(sizeof(oval_affected_t));
	if (affected == NULL)
		return NULL;

	affected->model = model;
	affected->family = OVAL_AFCFML_UNKNOWN;
	affected->platforms = oval_collection_new();
	affected->products = oval_collection_new();
	return affected;
}

struct oval_affected *oval_affected_clone(struct oval_definition_model *new_model, struct oval_affected *old_affected)
{
	__attribute__nonnull__(old_affected);

	struct oval_affected *new_affected = oval_affected_new(new_model);

	oval_affected_set_family(new_affected, old_affected->family);

	struct oval_string_iterator *platforms = oval_affected_get_platforms(old_affected);
	while (oval_string_iterator_has_more(platforms)) {
		char *platform = oval_string_iterator_next(platforms);
		oval_affected_add_platform(new_affected, platform);
	}
	oval_string_iterator_free(platforms);

	struct oval_string_iterator *products = oval_affected_get_products(old_affected);
	while (oval_string_iterator_has_more(products)) {
		char *product = oval_string_iterator_next(products);
		oval_affected_add_product(new_affected, product);
	}
	oval_string_iterator_free(products);

	return new_affected;
}

void oval_affected_free(struct oval_affected *affected)
{
	__attribute__nonnull__(affected);

	oval_collection_free_items(affected->platforms, (oscap_destruct_func) free);
	affected->platforms = NULL;
	oval_collection_free_items(affected->products, (oscap_destruct_func) free);
	affected->products = NULL;
	free(affected);
}

void oval_affected_set_family(struct oval_affected *affected, oval_affected_family_t family)
{
	__attribute__nonnull__(affected);

	affected->family = family;
}

void oval_affected_add_platform(struct oval_affected *affected, char *platform)
{
	__attribute__nonnull__(affected);

	oval_collection_add(affected->platforms, (void *)oscap_strdup(platform));
}

void oval_affected_add_product(struct oval_affected *affected, char *product)
{
	__attribute__nonnull__(affected);

	oval_collection_add(affected->products, (void *)oscap_strdup(product));
}

static oval_affected_family_t _odafamily(char *family)
{
	return oscap_string_to_enum(OVAL_ODAFAMILY_MAP, family);
}

const char *oval_affected_family_get_text(oval_affected_family_t family)
{
	return oscap_enum_to_string(OVAL_ODAFAMILY_MAP, family);
}

static int _oval_affected_parse_tag(xmlTextReaderPtr reader, struct oval_parser_context *context, void *user)
{
	struct oval_affected *affected = (struct oval_affected *)user;
	int return_code;
	xmlChar *tagname = xmlTextReaderLocalName(reader);
	//xmlChar *namespace = xmlTextReaderNamespaceUri(reader);

	if (strcmp((char *)tagname, "platform") == 0) {
		char *platform = NULL;
		return_code = oscap_parser_text_value(reader, &oscap_text_consumer, &platform);
		if (platform != NULL) {
			oval_affected_add_platform(affected, platform);
			free(platform);
		}
	} else if (strcmp((char *)tagname, "product") == 0) {
		char *product = NULL;
		return_code = oscap_parser_text_value(reader, &oscap_text_consumer, &product);
		if (product != NULL) {
			oval_affected_add_product(affected, product);
			free(product);
		}
	} else {
		dD("Skipping tag: %s", tagname);
		return_code = oval_parser_skip_tag(reader, context);
	}
	free(tagname);
	return return_code;
}

int oval_affected_parse_tag(xmlTextReaderPtr reader, struct oval_parser_context *context, oval_affected_consumer consumer, void *user)
{
	__attribute__nonnull__(context);

	struct oval_affected *affected = oval_affected_new(context->definition_model);
	char *family = (char *)xmlTextReaderGetAttribute(reader, BAD_CAST "family");
	oval_affected_set_family(affected, _odafamily(family));
	free(family);
	(*consumer) (affected, user);
	return oval_parser_parse_tag(reader, context, &_oval_affected_parse_tag, affected);
}

