/**
 * @file cpelang.c
 * \brief Interface to Common Platform Enumeration (CPE) Language
 *
 * See more details at http://nvd.nist.gov/cpe.cfm
 */

/*
 * Copyright 2009 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:
 *      Lukas Kuklinek <lkuklinek@redhat.com>
 *      Maros Barabas <mbarabas@redhat.com>
 */

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

#include <libxml/tree.h>
#include <string.h>

#include "public/cpe_lang.h"
#include "cpelang_priv.h"
#include "common/util.h"
#include "common/list.h"
#include "source/public/oscap_source.h"

#define CPE_LANG_SUPPORTED "2.2"

void cpe_lang_model_export(const struct cpe_lang_model *spec, const char *file)
{

	__attribute__nonnull__(spec);
	__attribute__nonnull__(file);

	if (file == NULL)
		return;

	cpe_lang_model_export_xml(spec, file);
}

const char * cpe_lang_model_supported(void)
{
        return CPE_LANG_SUPPORTED;
}

char *cpe_lang_model_detect_version_priv(xmlTextReader *reader)
{
	// FIXME: There is no detection logic in there, since there is only
	//        one version of CPE language so we just return that.
	return oscap_strdup("2.3");
}

/*
bool cpe_language_match_str(const char* cpe, const struct cpe_platform* platform)
{

        __attribute__nonnull__(cpe);
        __attribute__nonnull__(platform);

	struct cpe_name* cpe_;
	bool ret;

	cpe_ = cpe_new(cpe);
	if (cpe_ == NULL) return false;
	ret = cpe_language_match_cpe(cpe_, platform);
	cpe_free(cpe_);
	return ret;
}
*/

static bool cpe_testexpr_evaluate(const struct cpe_testexpr* expr, cpe_check_fn check_cb, cpe_dict_fn dict_cb, void* usr)
{
	bool ret = false;

	switch (cpe_testexpr_get_oper(expr) & CPE_LANG_OPER_MASK) {
	case CPE_LANG_OPER_AND:
		ret = true;
		OSCAP_FOREACH(cpe_testexpr, cur, cpe_testexpr_get_meta_expr(expr),
			if (!cpe_testexpr_evaluate(cur, check_cb, dict_cb, usr)) {
				ret = false;
				break;
			}
		)
		break;
	case CPE_LANG_OPER_OR:
		OSCAP_FOREACH(cpe_testexpr, cur, cpe_testexpr_get_meta_expr(expr),
			if (cpe_testexpr_evaluate(cur, check_cb, dict_cb, usr)) {
				ret = true;
				break;
			}
		)
		break;
	case CPE_LANG_OPER_MATCH:
		ret = dict_cb(cpe_testexpr_get_meta_cpe(expr), usr);
		break;
	case CPE_LANG_OPER_CHECK:
		ret = check_cb(cpe_testexpr_get_meta_check_system(expr), cpe_testexpr_get_meta_check_href(expr), cpe_testexpr_get_meta_check_id(expr), usr);
		break;
	default:
		assert(false);
	}

	return (cpe_testexpr_get_oper(expr) & CPE_LANG_OPER_NOT) ? !ret : ret;
}

bool cpe_platform_applicable_lang_model(const char* platform, struct cpe_lang_model *lang_model, cpe_check_fn check_cb, cpe_dict_fn dict_fn, void* usr)
{
	struct cpe_platform* plat = cpe_lang_model_get_item(lang_model, platform);

	if (plat == NULL) // can't find any matching platform implies not applicable
		return false;

	const struct cpe_testexpr* expr = cpe_platform_get_expr(plat);
	return cpe_testexpr_evaluate(expr, check_cb, dict_fn, usr);
}
