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

#include "handle.h"
#include "private.h"
#include "debug.h"
#include "log.h"

#include <sepol/booleans.h>
#include <sepol/policydb/hashtab.h>
#include <sepol/policydb/policydb.h>
#include <sepol/policydb/conditional.h>
#include "boolean_internal.h"

static int bool_update(sepol_handle_t * handle,
		       policydb_t * policydb,
		       const sepol_bool_key_t * key, const sepol_bool_t * data)
{

	const char *cname;
	char *name;
	int value;

	sepol_bool_key_unpack(key, &cname);
	name = strdup(cname);
	value = sepol_bool_get_value(data);

	if (!name)
		goto omem;

	cond_bool_datum_t *datum =
	    hashtab_search(policydb->p_bools.table, name);
	if (!datum) {
		LOGD("boolean %s no longer in policy", name);
		goto err;
	}
	if (value != 0 && value != 1) {
		LOGD("illegal value %d for boolean %s", value, name);
		goto err;
	}

	free(name);
	datum->state = value;
	return STATUS_SUCCESS;

      omem:
	LOGD("out of memory");

      err:
	free(name);
	LOGD("could not update boolean %s", cname);
	return STATUS_ERR;
}

static int bool_to_record(sepol_handle_t * handle,
			  const policydb_t * policydb,
			  int bool_idx, sepol_bool_t ** record)
{

	const char *name = policydb->p_bool_val_to_name[bool_idx];
	cond_bool_datum_t *booldatum = policydb->bool_val_to_struct[bool_idx];
	int value = booldatum->state;

	sepol_bool_t *tmp_record = NULL;

	if (sepol_bool_create(handle, &tmp_record) < 0)
		goto err;

	if (sepol_bool_set_name(handle, tmp_record, name) < 0)
		goto err;

	sepol_bool_set_value(tmp_record, value);

	*record = tmp_record;
	return STATUS_SUCCESS;

      err:
	LOGD("could not convert boolean %s to record", name);
	sepol_bool_free(tmp_record);
	return STATUS_ERR;
}

int sepol_bool_set(sepol_handle_t * handle,
		   sepol_policydb_t * p,
		   const sepol_bool_key_t * key, const sepol_bool_t * data)
{

	const char *name;
	sepol_bool_key_unpack(key, &name);

	policydb_t *policydb = &p->p;
	if (bool_update(handle, policydb, key, data) < 0)
		goto err;

	if (evaluate_conds(policydb) < 0) {
		LOGD("error while re-evaluating conditionals");
		goto err;
	}

	return STATUS_SUCCESS;

      err:
	LOGD("could not set boolean %s", name);
	return STATUS_ERR;
}

int sepol_bool_count(sepol_handle_t * handle __attribute__ ((unused)),
		     const sepol_policydb_t * p, unsigned int *response)
{

	const policydb_t *policydb = &p->p;
	*response = policydb->p_bools.nprim;

	handle = NULL;
	return STATUS_SUCCESS;
}

int sepol_bool_exists(sepol_handle_t * handle,
		      const sepol_policydb_t * p,
		      const sepol_bool_key_t * key, int *response)
{

	const policydb_t *policydb = &p->p;

	const char *cname;
	char *name = NULL;
	sepol_bool_key_unpack(key, &cname);
	name = strdup(cname);

	if (!name) {
		LOGD("out of memory, could not check "
		    "if user %s exists", cname);
		return STATUS_ERR;
	}

	*response = (hashtab_search(policydb->p_bools.table, name) != NULL);
	free(name);
	return STATUS_SUCCESS;
}

int sepol_bool_query(sepol_handle_t * handle,
		     const sepol_policydb_t * p,
		     const sepol_bool_key_t * key, sepol_bool_t ** response)
{

	const policydb_t *policydb = &p->p;
	cond_bool_datum_t *booldatum = NULL;

	const char *cname;
	char *name = NULL;
	sepol_bool_key_unpack(key, &cname);
	name = strdup(cname);

	if (!name)
		goto omem;

	booldatum = hashtab_search(policydb->p_bools.table, name);
	if (!booldatum) {
		*response = NULL;
		return STATUS_SUCCESS;
	}

	if (bool_to_record(handle, policydb,
			   booldatum->s.value - 1, response) < 0)
		goto err;

	free(name);
	return STATUS_SUCCESS;

      omem:
	LOGD("out of memory");

      err:
	LOGD("could not query boolean %s", cname);
	free(name);
	return STATUS_ERR;
}

int sepol_bool_iterate(sepol_handle_t * handle,
		       const sepol_policydb_t * p,
		       int (*fn) (const sepol_bool_t * boolean,
				  void *fn_arg), void *arg)
{

	const policydb_t *policydb = &p->p;
	unsigned int nbools = policydb->p_bools.nprim;
	sepol_bool_t *boolean = NULL;
	unsigned int i;

	/* For each boolean */
	for (i = 0; i < nbools; i++) {

		int status;

		if (bool_to_record(handle, policydb, i, &boolean) < 0)
			goto err;

		/* Invoke handler */
		status = fn(boolean, arg);
		if (status < 0)
			goto err;

		sepol_bool_free(boolean);
		boolean = NULL;

		/* Handler requested exit */
		if (status > 0)
			break;
	}

	return STATUS_SUCCESS;

      err:
	LOGD("could not iterate over booleans");
	sepol_bool_free(boolean);
	return STATUS_ERR;
}
