/*
 * Copyright 2010--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:
 *      Maros Barabas <mbarabas@redhat.com>
 *      Šimon Lukašík
 */

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

/* OVAL & OSCAP common */
#include "oscap_source.h"
#if defined(OVAL_PROBES_ENABLED)
# include <oval_probe.h>
# include "probe-table.h"
#endif
#include <oval_agent_api.h>
#include <oval_session.h>
#include <oval_results.h>
#include <oval_variables.h>
#include <ds_sds_session.h>
#include <assert.h>
#include <limits.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif

#include "oscap-tool.h"
#include "scap_ds.h"
#include <oscap_debug.h>

#if defined(OVAL_PROBES_ENABLED)
static int app_collect_oval(const struct oscap_action *action);
static int app_evaluate_oval(const struct oscap_action *action);
#endif
static int app_oval_validate(const struct oscap_action *action);
static int app_oval_xslt(const struct oscap_action *action);
static int app_analyse_oval(const struct oscap_action *action);

#if defined(OVAL_PROBES_ENABLED)
static bool getopt_oval_eval(int argc, char **argv, struct oscap_action *action);
static bool getopt_oval_collect(int argc, char **argv, struct oscap_action *action);
#endif
static bool getopt_oval_analyse(int argc, char **argv, struct oscap_action *action);
static bool getopt_oval_validate(int argc, char **argv, struct oscap_action *action);
static bool getopt_oval_report(int argc, char **argv, struct oscap_action *action);


static bool valid_inputs(const struct oscap_action *action);

#define OVAL_SUBMODULES_NUM	7
#define OVAL_GEN_SUBMODULES_NUM 2 /* See actual OVAL_GEN_SUBMODULES and
				OVAL_SUBMODULES arrays initialization below. */
static struct oscap_module* OVAL_SUBMODULES[OVAL_SUBMODULES_NUM];
static struct oscap_module* OVAL_GEN_SUBMODULES[OVAL_GEN_SUBMODULES_NUM];

struct oscap_module OSCAP_OVAL_MODULE = {
    .name = "oval",
    .parent = &OSCAP_ROOT_MODULE,
    .summary = "Open Vulnerability and Assessment Language",
    .submodules = OVAL_SUBMODULES
};

static struct oscap_module OVAL_VALIDATE = {
    .name = "validate",
    .parent = &OSCAP_OVAL_MODULE,
    .summary = "Validate OVAL XML content",
    .usage = "[options] oval-file.xml",
    .help =
	"Options:\n"
	"   --definitions                 - Validate OVAL Definitions\n"
	"   --variables                   - Validate external OVAL Variables\n"
	"   --syschar                     - Validate OVAL System Characteristics\n"
	"   --results                     - Validate OVAL Results\n"
	"   --skip-schematron             - Do not use schematron-based validation in addition to XML Schema\n",
    .opt_parser = getopt_oval_validate,
    .func = app_oval_validate
};

#if defined(OVAL_PROBES_ENABLED)
static struct oscap_module OVAL_EVAL = {
    .name = "eval",
    .parent = &OSCAP_OVAL_MODULE,
    .summary = "Probe the system and evaluate definitions from OVAL Definition file",
    .usage = "[options] oval-definitions.xml",
    .help =
	"Options:\n"
	"   --id <definition-id>          - ID of the definition we want to evaluate.\n"
	"   --variables <file>            - Provide external variables expected by OVAL Definitions.\n"
	"   --directives <file>           - Use OVAL Directives content to specify desired results content.\n"
	"   --without-syschar             - Don't provide system characteristic in result file.\n"
	"   --results <file>              - Write OVAL Results into file.\n"
	"   --report <file>               - Create human readable (HTML) report from OVAL Results.\n"
	"   --skip-valid                  - Skip validation.\n"
	"   --skip-validation\n"
	"   --datastream-id <id>          - ID of the data stream in the collection to use.\n"
	"                                   (only applicable for source data streams)\n"
	"   --oval-id <id>                - ID of the OVAL component ref in the data stream to use.\n"
	"                                   (only applicable for source data streams)\n"
	"   --fetch-remote-resources      - Download remote content referenced by OVAL Definitions.\n"
	"                                   (only applicable for source data streams)\n",
    .opt_parser = getopt_oval_eval,
    .func = app_evaluate_oval
};

static struct oscap_module OVAL_COLLECT = {
    .name = "collect",
    .parent = &OSCAP_OVAL_MODULE,
    .summary = "Probe the system and create system characteristics",
    .usage = "[options] oval-definitions.xml",
    .help =
	"Options:\n"
	"   --id <object>                 - Collect system characteristics ONLY for specified OVAL Object.\n"
	"   --syschar <file>              - Write OVAL System Characteristic into file.\n"
	"   --variables <file>            - Provide external variables expected by OVAL Definitions.\n"
	"   --skip-valid                  - Skip validation.\n"
	"   --skip-validation\n",
    .opt_parser = getopt_oval_collect,
    .func = app_collect_oval
};
#endif /* OVAL_PROBES_ENABLED */

static struct oscap_module OVAL_ANALYSE = {
    .name = "analyse",
    .parent = &OSCAP_OVAL_MODULE,
    .summary = "Evaluate provided system characteristics file",
    .usage = "[options] --results FILE oval-definitions.xml system-characteristics.xml" ,
    .help =
	"Options:\n"
	"   --variables <file>            - Provide external variables expected by OVAL Definitions.\n"
	"   --directives <file>           - Use OVAL Directives content to specify desired results content.\n"
	"   --skip-valid                  - Skip validation.\n"
	"   --skip-validation\n",
    .opt_parser = getopt_oval_analyse,
    .func = app_analyse_oval
};

static struct oscap_module OVAL_GENERATE = {
    .name = "generate",
    .parent = &OSCAP_OVAL_MODULE,
    .summary = "Convert an OVAL file to other formats",
    .usage_extra = "<subcommand> [sub-options] oval-file.xml",
    .submodules = OVAL_GEN_SUBMODULES
};

static struct oscap_module OVAL_REPORT = {
    .name = "report",
    .parent = &OVAL_GENERATE,
    .summary = "Generate a HTML report from OVAL results file",
    .usage = "[options] oval-file.xml",
    .help =
	"Options:\n"
	"   --output <file>               - Write the HTML into file.",
    .opt_parser = getopt_oval_report,
    .user = "oval-results-report.xsl",
    .func = app_oval_xslt
};

static struct oscap_module* OVAL_GEN_SUBMODULES[OVAL_GEN_SUBMODULES_NUM] = {
    &OVAL_REPORT,
    NULL
};

static struct oscap_module* OVAL_SUBMODULES[OVAL_SUBMODULES_NUM] = {
#if defined(OVAL_PROBES_ENABLED)
    &OVAL_COLLECT,
    &OVAL_EVAL,
#endif
    &OVAL_ANALYSE,
    &OVAL_VALIDATE,
    &OVAL_GENERATE,
    NULL
};

static int app_oval_callback(const struct oval_result_definition * res_def, void *arg)
{
	oval_result_t result =  oval_result_definition_get_result(res_def);

	printf("Definition %s: %s\n", oval_result_definition_get_id(res_def), oval_result_get_text(result));

	return 0;
}

#if defined(OVAL_PROBES_ENABLED)
int app_collect_oval(const struct oscap_action *action)
{
	struct oval_definition_model	*def_model = NULL;
	struct oval_variable_model	*var_model = NULL;
	struct oval_syschar_model	*sys_model = NULL;
	struct oval_sysinfo		*sysinfo   = NULL;
	struct oval_probe_session	*pb_sess   = NULL;
	struct oval_generator		*generator = NULL;
	int ret = OSCAP_ERROR;

	/* validate inputs */
	if (action->validate) {
		if (!valid_inputs(action)) {
			goto cleanup;
		}
	}

	/* import definitions */
	struct oscap_source *source = oscap_source_new_from_file(action->f_oval);
	def_model = oval_definition_model_import_source(source);
	oscap_source_free(source);
	if (def_model == NULL) {
		fprintf(stderr, "Failed to import the OVAL Definitions from '%s'.\n", action->f_oval);
		goto cleanup;
	}

	/* bind external variables */
	if(action->f_variables) {
		struct oscap_source *var_source = oscap_source_new_from_file(action->f_variables);
		var_model = oval_variable_model_import_source(var_source);
		oscap_source_free(var_source);
		if (var_model == NULL) {
			fprintf(stderr, "Failed to import the OVAL Variables from '%s'.\n", action->f_variables);
			goto cleanup;
		}

		if (oval_definition_model_bind_variable_model(def_model, var_model)) {
			fprintf(stderr, "Failed to bind Variables to Definitions\n");
			goto cleanup;
		}
	}

	/* create empty syschar model */
	sys_model = oval_syschar_model_new(def_model);

	/* set product name */
	generator = oval_syschar_model_get_generator(sys_model);
	oval_generator_set_product_name(generator, OSCAP_PRODUCTNAME);

	/* create probe session */
	pb_sess = oval_probe_session_new(sys_model);

	/* query sysinfo */
	ret = oval_probe_query_sysinfo(pb_sess, &sysinfo);
	if (ret != 0) {
		fprintf(stderr, "Failed to query sysinfo\n");
		goto cleanup;
	}
	oval_syschar_model_set_sysinfo(sys_model, sysinfo);

	/* query objects */
	struct oval_object *object;
	struct oval_syschar *syschar;
	oval_syschar_collection_flag_t sc_flg;
	if (action->id) {
		object = oval_definition_model_get_object(def_model, action->id);
		if (!object) {
			fprintf(stderr, "Object ID(%s) does not exist in '%s'.\n", action->id, action->f_oval);
			goto cleanup;
		}
		printf("Collected: \"%s\" : ", oval_object_get_id(object));
		oval_probe_query_object(pb_sess, object, 0, &syschar);
		sc_flg = oval_syschar_get_flag(syschar);
		printf("%s\n", oval_syschar_collection_flag_get_text(sc_flg));
	}
	else {
	        struct oval_object_iterator *objects = oval_definition_model_get_objects(def_model);
		while (oval_object_iterator_has_more(objects)) {
			object = oval_object_iterator_next(objects);
			printf("Collected: \"%s\" : ", oval_object_get_id(object));
			oval_probe_query_object(pb_sess, object, 0, &syschar);
			sc_flg = oval_syschar_get_flag(syschar);
			printf("%s\n", oval_syschar_collection_flag_get_text(sc_flg));
		}
		oval_object_iterator_free(objects);
	}

	const char* full_validation = getenv("OSCAP_FULL_VALIDATION");

	/* output */
	if (action->f_syschar != NULL) {
		/* export OVAL System Characteristics */
		oval_syschar_model_export(sys_model, action->f_syschar);

		/* validate OVAL System Characteristics */
		if (action->validate && full_validation) {
			struct oscap_source *syschar_source = oscap_source_new_from_file(action->f_syschar);
			if (oscap_source_validate(syschar_source, reporter, (void *)action)) {
				oscap_source_free(syschar_source);
				goto cleanup;
			}
			fprintf(stdout, "OVAL System Characteristics are exported correctly.\n");
			oscap_source_free(syschar_source);
		}
	}

	ret = OSCAP_OK;

cleanup:
	if(oscap_err())
		fprintf(stderr, "%s %s\n", OSCAP_ERR_MSG, oscap_err_desc());

	if (sysinfo) oval_sysinfo_free(sysinfo);
	if (pb_sess) oval_probe_session_destroy(pb_sess);
	if (sys_model) oval_syschar_model_free(sys_model);
	if (def_model) oval_definition_model_free(def_model);

	return ret;
}

int app_evaluate_oval(const struct oscap_action *action)
{
	struct oval_session *session = NULL;
	oval_result_t eval_result;
	int ret = OSCAP_ERROR;

	/* create a new OVAL session */
	if ((session = oval_session_new(action->f_oval)) == NULL) {
		oscap_print_error();
		return ret;
	}

	/* set validation level */
	oval_session_set_validation(session, action->validate, getenv("OSCAP_FULL_VALIDATION") != NULL);

	/* set source DS related IDs */
	oval_session_set_datastream_id(session, action->f_datastream_id);
	oval_session_set_component_id(session, action->f_oval_id);

	/* set reporter function for XML validation of inputs and outputs */
	oval_session_set_xml_reporter(session, reporter);

	/* set OVAL Variables */
	oval_session_set_variables(session, action->f_variables);

	oval_session_set_remote_resources(session, action->remote_resources, download_reporting_callback);
	/* load all necesary OVAL Definitions and bind OVAL Variables if provided */
	if ((oval_session_load(session)) != 0)
		goto cleanup;

	/* evaluation */
	if (action->id) {
		if ((oval_session_evaluate_id(session, action->id, &eval_result)) != 0)
			goto cleanup;
		printf("Definition %s: %s\n", action->id, oval_result_get_text(eval_result));
	}
	else {
		if ((oval_session_evaluate(session, app_oval_callback, NULL)) != 0)
			goto cleanup;
	}

	printf("Evaluation done.\n");

	oval_session_set_directives(session, action->f_directives);
	oval_session_set_results_export(session, action->f_results);
	oval_session_set_report_export(session, action->f_report);
	oval_session_set_export_system_characteristics(session, !action->without_sys_chars);
	if (oval_session_export(session) != 0)
		goto cleanup;

	ret = OSCAP_OK;

cleanup:
	oscap_print_error();
	oval_session_free(session);
	return ret;
}
#endif /* OVAL_PROBES_ENABLED */

static int app_analyse_oval(const struct oscap_action *action) {
	struct oval_definition_model	*def_model = NULL;
	struct oval_syschar_model	*sys_model = NULL;
	struct oval_results_model	*res_model = NULL;
	struct oval_variable_model	*var_model = NULL;
	struct oval_directives_model	*dir_model = NULL;
 	struct oval_syschar_model	*sys_models[2];
	struct oval_generator		*generator = NULL;
	int ret = OSCAP_ERROR;

	/* validate inputs */
	if (action->validate) {
		if (!valid_inputs(action)) {
			goto cleanup;
		}
	}

	/* load defnitions */
	struct oscap_source *source = oscap_source_new_from_file(action->f_oval);
	def_model = oval_definition_model_import_source(source);
	oscap_source_free(source);
        if (def_model == NULL) {
                fprintf(stderr, "Failed to import the OVAL Definitions from '%s'.\n", action->f_oval);
		goto cleanup;
        }

	/* bind external variables */
	if(action->f_variables) {
		struct oscap_source *var_source = oscap_source_new_from_file(action->f_variables);
		var_model = oval_variable_model_import_source(var_source);
		oscap_source_free(var_source);
		if (var_model == NULL) {
			fprintf(stderr, "Failed to import the OVAL Variables from '%s'.\n", action->f_variables);
			goto cleanup;
		}

		if (oval_definition_model_bind_variable_model(def_model, var_model)) {
			fprintf(stderr, "Failed to bind Variables to Definitions\n");
			goto cleanup;
		}
	}

	/* load system characteristics */
	sys_model = oval_syschar_model_new(def_model);
	source = oscap_source_new_from_file(action->f_syschar);
	if (oval_syschar_model_import_source(sys_model, source) ==  -1 ) {
                fprintf(stderr, "Failed to import the System Characteristics from '%s'.\n", action->f_syschar);
		oscap_source_free(source);
                goto cleanup;
        }
	oscap_source_free(source);

	/* evaluate */
	sys_models[0] = sys_model;
	sys_models[1] = NULL;
	res_model = oval_results_model_new(def_model, sys_models);

	/* set product name */
        generator = oval_results_model_get_generator(res_model);
        oval_generator_set_product_name(generator, OSCAP_PRODUCTNAME);
	oval_generator_set_product_version(generator, oscap_get_version());

	oval_results_model_eval(res_model);

	/* export results */
	if (action->f_results != NULL) {
		/* import directives */
		if (action->f_directives != NULL) {
			dir_model = oval_directives_model_new();
			struct oscap_source *dir_source = oscap_source_new_from_file(action->f_directives);
			oval_directives_model_import_source(dir_model, dir_source);
			oscap_source_free(dir_source);
		}

		/* export result model to XML */
		oval_results_model_export(res_model, dir_model, action->f_results);

		const char* full_validation = getenv("OSCAP_FULL_VALIDATION");

		/* validate OVAL Results */
		if (action->validate && full_validation) {
			struct oscap_source *result_source = oscap_source_new_from_file(action->f_results);
			if (oscap_source_validate(result_source, reporter, (void *) action)) {
				oscap_source_free(result_source);
				goto cleanup;
			}
			fprintf(stdout, "OVAL Results are exported correctly.\n");
			oscap_source_free(result_source);
		}
	}

	ret = OSCAP_OK;

	/* clean up */
cleanup:
	if(oscap_err())
		fprintf(stderr, "%s %s\n", OSCAP_ERR_MSG, oscap_err_desc());

	if(res_model) oval_results_model_free(res_model);
	if(sys_model) oval_syschar_model_free(sys_model);
	if(def_model) oval_definition_model_free(def_model);
	if(dir_model) oval_directives_model_free(dir_model);

	return ret;
}

static int app_oval_xslt(const struct oscap_action *action)
{
    assert(action->module->user);
    return app_xslt(action->f_oval, action->module->user, action->f_results, NULL);
}

enum oval_opt {
    OVAL_OPT_RESULT_FILE = 1,
    OVAL_OPT_REPORT_FILE,
    OVAL_OPT_ID,
    OVAL_OPT_VARIABLES,
    OVAL_OPT_SYSCHAR,
    OVAL_OPT_DIRECTIVES,
    OVAL_OPT_DATASTREAM_ID,
    OVAL_OPT_OVAL_ID,
	OVAL_OPT_OUTPUT = 'o'
};

#if defined(OVAL_PROBES_ENABLED)
bool getopt_oval_eval(int argc, char **argv, struct oscap_action *action)
{
	action->doctype = OSCAP_DOCUMENT_OVAL_DEFINITIONS;

	/* Command-options */
	struct option long_options[] = {
		{ "results", 	required_argument, NULL, OVAL_OPT_RESULT_FILE  },
		{ "report",  	required_argument, NULL, OVAL_OPT_REPORT_FILE  },
		{ "id",        	required_argument, NULL, OVAL_OPT_ID           },
		{ "variables",	required_argument, NULL, OVAL_OPT_VARIABLES    },
		{ "directives",	required_argument, NULL, OVAL_OPT_DIRECTIVES   },
		{ "without-syschar",	no_argument, &action->without_sys_chars, 1},
		{ "datastream-id",required_argument, NULL, OVAL_OPT_DATASTREAM_ID},
		{ "oval-id",    required_argument, NULL, OVAL_OPT_OVAL_ID},
		{ "skip-valid",	no_argument, &action->validate, 0 },
		{ "skip-validation",	no_argument, &action->validate, 0 },
		{ "fetch-remote-resources", no_argument, &action->remote_resources, 1},
		{ 0, 0, 0, 0 }
	};

	int c;
	while ((c = getopt_long(argc, argv, "o:", long_options, NULL)) != -1) {
		switch (c) {
		case OVAL_OPT_RESULT_FILE: action->f_results = optarg; break;
		case OVAL_OPT_REPORT_FILE: action->f_report  = optarg; break;
		case OVAL_OPT_ID: action->id = optarg; break;
		case OVAL_OPT_VARIABLES: action->f_variables = optarg; break;
		case OVAL_OPT_DIRECTIVES: action->f_directives = optarg; break;
		case OVAL_OPT_DATASTREAM_ID: action->f_datastream_id = optarg;	break;
		case OVAL_OPT_OVAL_ID: action->f_oval_id = optarg;	break;
		case 0: break;
		default: return oscap_module_usage(action->module, stderr, NULL);
		}
	}

	/* We should have Definitions file here */
	if (optind >= argc)
		return oscap_module_usage(action->module, stderr, "Definitions file is not specified!");
	action->f_oval = argv[optind];

	return true;
}
#endif /* OVAL_PROBES_ENABLED */

#if defined(OVAL_PROBES_ENABLED)
bool getopt_oval_collect(int argc, char **argv, struct oscap_action *action)
{
	action->doctype = OSCAP_DOCUMENT_OVAL_DEFINITIONS;

	/* Command-options */
	struct option long_options[] = {
		{ "id",        	required_argument, NULL, OVAL_OPT_ID           },
		{ "variables",	required_argument, NULL, OVAL_OPT_VARIABLES    },
		{ "syschar",	required_argument, NULL, OVAL_OPT_SYSCHAR      },
		{ "skip-valid",	no_argument, &action->validate, 0 },
		{ "skip-validation",	no_argument, &action->validate, 0 },
		{ 0, 0, 0, 0 }
	};

	int c;
	while ((c = getopt_long(argc, argv, "o:", long_options, NULL)) != -1) {
		switch (c) {
		case OVAL_OPT_ID: action->id = optarg; break;
		case OVAL_OPT_VARIABLES: action->f_variables = optarg; break;
		case OVAL_OPT_SYSCHAR: action->f_syschar = optarg; break;
		case 0: break;
		default: return oscap_module_usage(action->module, stderr, NULL);
		}
	}

	/* We should have Definitions file here */
	if (optind >= argc)
		return oscap_module_usage(action->module, stderr, "Definitions file is not specified!");
	action->f_oval = argv[optind];

	return true;
}
#endif /* OVAL_PROBES_ENABLED */

bool getopt_oval_analyse(int argc, char **argv, struct oscap_action *action)
{
	action->doctype = OSCAP_DOCUMENT_OVAL_DEFINITIONS;

	/* Command-options */
	struct option long_options[] = {
		{ "results", 	required_argument, NULL, OVAL_OPT_RESULT_FILE  },
		{ "variables",	required_argument, NULL, OVAL_OPT_VARIABLES    },
		{ "directives",	required_argument, NULL, OVAL_OPT_DIRECTIVES   },
		{ "skip-valid",	no_argument, &action->validate, 0 },
		{ "skip-validation",	no_argument, &action->validate, 0 },
		{ 0, 0, 0, 0 }
	};

	int c;
	while ((c = getopt_long(argc, argv, "o:", long_options, NULL)) != -1) {
		switch (c) {
		case OVAL_OPT_RESULT_FILE: action->f_results = optarg; break;
		case OVAL_OPT_VARIABLES: action->f_variables = optarg; break;
		case OVAL_OPT_DIRECTIVES: action->f_directives = optarg; break;
		case 0: break;
		default: return oscap_module_usage(action->module, stderr, NULL);
		}
	}

	/* We should have Definitions file here */
	if (optind >= argc)
		return oscap_module_usage(action->module, stderr, "Definitions file is not specified!");
	action->f_oval = argv[optind];

	if (action->module == &OVAL_ANALYSE) {
		/* We should have System Characteristics file here */
		if ((optind+1) > argc)
			return oscap_module_usage(action->module, stderr, "System characteristics file is not specified");
		action->f_syschar = argv[optind + 1];

		if (action->f_results == NULL) {
			return oscap_module_usage(action->module, stderr, "OVAL Results file is not specified(--results parameter)");
		}
	}

	return true;
}

bool getopt_oval_report(int argc, char **argv, struct oscap_action *action)
{
	action->doctype = OSCAP_DOCUMENT_OVAL_DEFINITIONS;

	/* Command-options */
	struct option long_options[] = {
		{ "output",    	required_argument, NULL, OVAL_OPT_OUTPUT       },
		{ 0, 0, 0, 0 }
	};

	/*
	 * it is not nice that we use action->f_results for output and
	 * action->f_oval as input here.
	 */
	int c;
	while ((c = getopt_long(argc, argv, "o:", long_options, NULL)) != -1) {
		switch (c) {
		case OVAL_OPT_OUTPUT: action->f_results = optarg; break;
		case 0: break;
		default: return oscap_module_usage(action->module, stderr, NULL);
		}
	}

	/* We should have oval results file here */
	if (optind >= argc)
		return oscap_module_usage(action->module, stderr, "Definitions file is not specified!");
	action->f_oval = argv[optind];

	return true;
}

bool getopt_oval_validate(int argc, char **argv, struct oscap_action *action)
{
	/* we assume 0 is unknown */
	action->doctype = 0;

	/* Command-options */
	struct option long_options[] = {
        // flags
		{ "definitions",	no_argument, &action->doctype, OSCAP_DOCUMENT_OVAL_DEFINITIONS },
		{ "variables",		no_argument, &action->doctype, OSCAP_DOCUMENT_OVAL_VARIABLES   },
		{ "syschar",		no_argument, &action->doctype, OSCAP_DOCUMENT_OVAL_SYSCHAR     },
		{ "results",		no_argument, &action->doctype, OSCAP_DOCUMENT_OVAL_RESULTS     },
		{ "directives",		no_argument, &action->doctype, OSCAP_DOCUMENT_OVAL_DIRECTIVES  },
		//TODO: force schematron validation (no-op, deprecate and remove)
		{ "schematron",		no_argument, &action->schematron, 1 },
		{ "skip-schematron",no_argument, &action->schematron, 0 },
        // end
		{ 0, 0, 0, 0 }
	};

	int c;
	while ((c = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
		switch (c) {
		case 0: break;
		default: return oscap_module_usage(action->module, stderr, NULL);
		}
	}

	/* we should have OVAL content here */
	if (optind >= argc)
		return oscap_module_usage(action->module, stderr, "OVAL file needs to be specified!");
	action->f_oval = argv[optind];

	return true;
}

static bool valid_inputs(const struct oscap_action *action) {
        bool result = true;

	/* validate SDS or OVAL Definitions & Variables & Syschars,
	   depending on the data */
	struct oscap_source *definitions_source = oscap_source_new_from_file(action->f_oval);
	if (oscap_source_get_scap_type(definitions_source) != OSCAP_DOCUMENT_OVAL_DEFINITIONS &&
			oscap_source_get_scap_type(definitions_source) != OSCAP_DOCUMENT_SDS) {
		fprintf(stderr, "Type mismatch: %s. Expecting OVAL Definition or source data stream, but found %s.\n",
			action->f_oval, oscap_document_type_to_string(oscap_source_get_scap_type(definitions_source)));
		result = false;
	}
	if (oscap_source_validate(definitions_source, reporter, (void *) action)) {
		result = false;
	}
	oscap_source_free(definitions_source);

	if (action->f_variables) {
		struct oscap_source *variables_source = oscap_source_new_from_file(action->f_variables);
		if (oscap_source_get_scap_type(variables_source) != OSCAP_DOCUMENT_OVAL_VARIABLES) {
			fprintf(stderr, "Type mismatch: %s. Expecting OVAL Variables, but found %s.\n",
				action->f_variables, oscap_document_type_to_string(oscap_source_get_scap_type(variables_source)));
			result = false;
		}
		if (oscap_source_validate(variables_source, reporter, (void *) action)) {
			result = false;
		}
		oscap_source_free(variables_source);
	}

	if (action->f_directives) {
		struct oscap_source *directives_source = oscap_source_new_from_file(action->f_directives);
		if (oscap_source_get_scap_type(directives_source) != OSCAP_DOCUMENT_OVAL_DIRECTIVES) {
			fprintf(stderr, "Type mismatch: %s. Expecting OVAL Directives, but found %s.\n",
				action->f_directives, oscap_document_type_to_string(oscap_source_get_scap_type(directives_source)));
			result = false;
		}
		if (oscap_source_validate(directives_source, reporter, (void *) action)) {
			result = false;
		}
		oscap_source_free(directives_source);
	}

	if (action->module == &OVAL_ANALYSE && action->f_syschar) {
		struct oscap_source *syschar_source = oscap_source_new_from_file(action->f_syschar);
		if (oscap_source_get_scap_type(syschar_source) != OSCAP_DOCUMENT_OVAL_SYSCHAR) {
			fprintf(stderr, "Type mismatch: %s. Expecting OVAL System Characteristic, but found %s.\n",
				action->f_syschar, oscap_document_type_to_string(oscap_source_get_scap_type(syschar_source)));
			result = false;
		}
		if (oscap_source_validate(syschar_source, reporter, (void *) action)) {
			result = false;
		}
		oscap_source_free(syschar_source);
	}

	return result;
}

static int app_oval_validate(const struct oscap_action *action) {
	int ret;
	int result = OSCAP_OK;

	struct oscap_source *source = oscap_source_new_from_file(action->f_oval);
	ret = oscap_source_validate(source, reporter, (void *) action);

	if (ret < 0) {
		result = OSCAP_ERROR;
	} else if (ret > 0) {
		result = OSCAP_FAIL;
	} else {
		// We can only do schematron validation if the file isn't a source datastream
		if (action->schematron && oscap_source_get_scap_type(source) != OSCAP_DOCUMENT_SDS) {
			ret = oscap_source_validate_schematron(source, NULL);
			if (ret < 0) {
				result = OSCAP_ERROR;
			} else if (ret > 0) {
				result = OSCAP_FAIL;
			}
		}
	}

	oscap_source_free(source);
	oscap_print_error();

	return result;
}

