/**
 * @addtogroup PROBEAPI
 * @{
 * @file   probe-api.c
 * @brief  Probe API implmentation
 * @author "Daniel Kopecek" <dkopecek@redhat.com>
 * @author "Tomas Heinrich" <theinric@redhat.com>
 */
/*
 * Copyright 2009-2011 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:
 *      Daniel Kopecek <dkopecek@redhat.com>
 *      Tomas Heinrich <theinric@redhat.com>
 */

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

#include <stdarg.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#ifdef OS_WINDOWS
#include <winsock2.h>
#include <in6addr.h>
#include <ws2tcpip.h>
#else
#include <arpa/inet.h> /* inet_pton() in probe_ent_from_cstr() */
#include <netinet/in.h>
#include <sys/socket.h>
#endif

#include "debug_priv.h"
#include "_probe-api.h"
#include "oval_probe_impl.h"
#include "probe/entcmp.h"
#include "probe/probe.h"
#include "SEAP/generic/strto.h"
#include "oscap_helpers.h"

extern probe_rcache_t  *OSCAP_GSYM(pcache);
extern probe_ncache_t  *OSCAP_GSYM(ncache);
extern struct id_desc_t OSCAP_GSYM(id_desc);
extern probe_option_t *OSCAP_GSYM(probe_optdef);
extern size_t OSCAP_GSYM(probe_optdef_count);

/*
 * items
 */
SEXP_t *probe_item_creat(const char *name, SEXP_t * attrs, ...)
{
	va_list ap;
	SEXP_t *itm, *ns, *val, *ent;

	va_start(ap, attrs);

	itm = probe_item_new(name, attrs);
	name = va_arg(ap, const char *);

	while (name != NULL) {
		attrs = va_arg(ap, SEXP_t *);
		val = va_arg(ap, SEXP_t *);

                ns  = probe_ncache_ref (OSCAP_GSYM(ncache), name);
		ent = SEXP_list_new(NULL);

		if (attrs != NULL) {
			SEXP_t *nl, *nj;

			nl = SEXP_list_new(ns, NULL);
			nj = SEXP_list_join(nl, attrs);

			SEXP_list_add(ent, nj);
			SEXP_free(nl);
			SEXP_free(nj);
		} else
			SEXP_list_add(ent, ns);

		SEXP_free(ns);

		SEXP_list_add(ent, val);
		SEXP_list_add(itm, ent);

		SEXP_free(ent);

		name = va_arg(ap, const char *);
	}

	va_end(ap);

	return (itm);
}

SEXP_t *probe_item_new(const char *name, SEXP_t * attrs)
{
	SEXP_t *itm, *sid, *attr;

        /*
         * Allocate space for the ID which will be generated
         * in the icache worker
         */
	sid  = SEXP_string_new("", 0);
	attr = probe_attr_creat("id", sid, NULL);

	if (attrs != NULL) {
		attrs = SEXP_list_join(attr, attrs);
		SEXP_free(attr);
	} else
		attrs = attr;
	/*
	 * Objects have the same structure as items.
	 */
	itm = probe_obj_new(name, attrs);
	SEXP_free(sid);
	SEXP_free(attrs);

	return itm;
}

SEXP_t *probe_item_attr_add(SEXP_t * item, const char *name, SEXP_t * val)
{
	SEXP_t *n_ref, *ns;

	if (val == NULL)
		ns = SEXP_string_new(name, strlen(name));
	else
		ns = SEXP_string_newf(":%s", name);

	n_ref = SEXP_listref_first(item);

	if (SEXP_listp(n_ref)) {
		/*
		 * There are already some attributes.
		 * Just add the new to the list.
		 */
		SEXP_list_add(n_ref, ns);

		if (val != NULL)
			SEXP_list_add(n_ref, val);
	} else {
		/*
		 * There aren't attributes in this item.
		 * We need to replace the item name S-exp
		 * with a list containing the item name
		 * S-exp and the attribute.
		 */
		SEXP_t *nl;
		SEXP_t *x_ref;

		x_ref = SEXP_list_nth(item, 1);
		nl = SEXP_list_new(x_ref, ns, val, NULL);
		SEXP_free(x_ref);

		x_ref = SEXP_list_replace(item, 1, nl);
		SEXP_free(x_ref);
		SEXP_free(nl);
	}

	SEXP_free(n_ref);

	SEXP_free(ns);

	return (val);
}

SEXP_t *probe_item_ent_add(SEXP_t *item, const char *name, SEXP_t *attrs, SEXP_t *val)
{
	SEXP_t *ent;

	ent = probe_ent_creat1(name, attrs, val);
	SEXP_list_add(item, ent);
	SEXP_free(ent);

	return (item);
}

int probe_item_setstatus(SEXP_t *obj, oval_syschar_status_t status)
{
        SEXP_t *r0;

	_A(obj != NULL);

	probe_item_attr_add(obj, "status", r0 = SEXP_number_newi_32((int) status));
        SEXP_free(r0);

	return (0);
}

int probe_itement_setstatus(SEXP_t *obj, const char *name, uint32_t n, oval_syschar_status_t status)
{
        SEXP_t *ent_h, *ent_s, *r0;

        ent_h = probe_item_getent (obj, name, n);
        ent_s = SEXP_unref (ent_h);

        _A(ent_s != NULL);

        probe_ent_attr_add (ent_s, "status", r0 = SEXP_number_newi_32 ((int) status));
        SEXP_free (ent_s);
        SEXP_free (r0);

        return (0);
}

void probe_item_resetidctr(struct id_desc_t *id_desc)
{
	id_desc->item_id_ctr = 1;
}

bool probe_item_filtered(const SEXP_t *item, const SEXP_t *filters)
{
	bool filtered = false;
	SEXP_t *filter, *ste;

	SEXP_list_foreach(filter, filters) {
		SEXP_t *felm, *ste_res, *r0;
		oval_result_t ores;
		oval_operator_t oopr;
		oval_filter_action_t ofact;

		r0 = SEXP_list_first(filter);
		ofact = SEXP_number_getu(r0);
		SEXP_free(r0);
		ste = SEXP_list_nth(filter, 2);
		ste_res = SEXP_list_new(NULL);

		SEXP_sublist_foreach(felm, ste, 2, SEXP_LIST_END) {
			SEXP_t *ielm, *elm_res;
			char *elm_name;
			oval_check_t ochk;
			int i;

			elm_res = SEXP_list_new(NULL);
			elm_name = probe_ent_getname(felm);

			for (i = 1;; ++i) {
				ielm = probe_obj_getent(item, elm_name, i);

				if (ielm == NULL)
					break;

				ores = probe_entste_cmp(felm, ielm);
				SEXP_list_add(elm_res, r0 = SEXP_number_newi_32(ores));

				SEXP_free(ielm);
				SEXP_free(r0);
			}

			if (SEXP_list_length(elm_res) > 0) {
				r0 = probe_ent_getattrval(felm, "entity_check");

				if (r0 == NULL)
					ochk = OVAL_CHECK_ALL;
				else
					ochk = SEXP_number_geti_32(r0);

				SEXP_free(r0);

				ores = probe_ent_result_bychk(elm_res, ochk);
			} else {
				ores = OVAL_RESULT_FALSE;
			}
			SEXP_list_add(ste_res, r0 = SEXP_number_newi_32(ores));
			SEXP_free(r0);
			SEXP_free(elm_res);
			free(elm_name);
		}

		r0 = probe_ent_getattrval(ste, "operator");
		if (r0 == NULL)
			oopr = OVAL_OPERATOR_AND;
		else
			oopr = SEXP_number_geti_32(r0);
		ores = probe_ent_result_byopr(ste_res, oopr);
		SEXP_free(ste);
		SEXP_free(ste_res);
		SEXP_free(r0);

		if ((ores == OVAL_RESULT_TRUE && ofact == OVAL_FILTER_ACTION_EXCLUDE)
		    || (ores == OVAL_RESULT_FALSE && ofact == OVAL_FILTER_ACTION_INCLUDE)) {
			filtered = true;
			SEXP_free(filter);
			break;
		}
	}

	return filtered;
}

/*
 * attributes
 */

SEXP_t *probe_attr_creat(const char *name, const SEXP_t * val, ...)
{
	va_list ap;
	SEXP_t *list, *ns;

	va_start(ap, val);
	list = SEXP_list_new(NULL);

	while (name != NULL) {
		if (val == NULL) {
			ns = SEXP_string_new(name, strlen(name));
			SEXP_list_add(list, ns);
			SEXP_free(ns);
		} else {
			ns = SEXP_string_newf(":%s", name);
			SEXP_list_add(list, ns);
			SEXP_list_add(list, val);
			SEXP_free(ns);
		}

		name = va_arg(ap, const char *);
		val = va_arg(ap, SEXP_t *);
	}

	va_end(ap);

	return (list);
}

/*
 * objects
 */

SEXP_t *probe_obj_creat(const char *name, SEXP_t * attrs, ...)
{
	va_list ap;
	SEXP_t *obj, *ns, *val, *ent;

	va_start(ap, attrs);

	obj = probe_obj_new(name, attrs);
	name = va_arg(ap, const char *);

	while (name != NULL) {
		attrs = va_arg(ap, SEXP_t *);
		val = va_arg(ap, SEXP_t *);

                ns  = probe_ncache_ref (OSCAP_GSYM(ncache), name);
		ent = SEXP_list_new(NULL);

		if (attrs != NULL) {
			SEXP_t *nl, *nj;

			nl = SEXP_list_new(ns, NULL);
			nj = SEXP_list_join(nl, attrs);

			SEXP_list_add(ent, nj);
			SEXP_free(nl);
			SEXP_free(nj);
		} else
			SEXP_list_add(ent, ns);

		SEXP_free(ns);

		SEXP_list_add(ent, val);
		SEXP_list_add(obj, ent);

		SEXP_free(ent);

		name = va_arg(ap, const char *);
	}

	va_end(ap);
	return (obj);
}

SEXP_t *probe_obj_new(const char *name, SEXP_t * attrs)
{
	SEXP_t *obj, *ns;

	obj = SEXP_list_new(NULL);
	ns  = probe_ncache_ref (OSCAP_GSYM(ncache), name);

	if (attrs != NULL) {
		SEXP_t *nl, *nj;

		nl = SEXP_list_new(ns, NULL);
		nj = SEXP_list_join(nl, attrs);

		SEXP_list_add(obj, nj);
		SEXP_free(nl);
                SEXP_free(nj);
	} else
		SEXP_list_add(obj, ns);

	SEXP_free(ns);

	return (obj);
}

SEXP_t *probe_obj_getent(const SEXP_t * obj, const char *name, uint32_t n)
{
	SEXP_t *objents, *ent, *ent_name;

	_A(obj != NULL);
	_A(name != NULL);
	_A(n > 0);

	ent = NULL;
	objents = SEXP_list_rest(obj);

	SEXP_list_foreach(ent, objents) {
		ent_name = SEXP_list_first(ent);

		if (SEXP_listp(ent_name)) {
			SEXP_t *nr;

			nr = SEXP_list_first(ent_name);
			SEXP_free(ent_name);
			ent_name = nr;
		}

		if (SEXP_stringp(ent_name)) {
#if !defined(NDEBUG) && defined(SEAP_VERBOSE_DEBUG)
			char buf[128];
			SEXP_string_cstr_r(ent_name, buf, sizeof buf);
			dD("1=\"%s\", 2=\"%s\", n=%u", buf, name, n);
#endif

			if (SEXP_strcmp(ent_name, name) == 0 && (--n == 0)) {
				SEXP_free(ent_name);
				break;
			}
		}

		SEXP_free(ent_name);
	}

	SEXP_free(objents);

	return (ent);
}

SEXP_t *probe_obj_getentval(const SEXP_t * obj, const char *name, uint32_t n)
{
	SEXP_t *ent, *val;

	ent = probe_obj_getent(obj, name, n);
	val = probe_ent_getval(ent);

	SEXP_free(ent);

	return (val);
}

int probe_obj_getentvals(const SEXP_t * obj, const char *name, uint32_t n, SEXP_t ** res)
{
	SEXP_t *ent;
	int ret;

	ent = probe_obj_getent(obj, name, n);
	ret = probe_ent_getvals(ent, res);

	SEXP_free(ent);

	return (ret);
}

oval_schema_version_t probe_obj_get_platform_schema_version(const SEXP_t *obj)
{
	SEXP_t *sexp_ver;

	if (obj == NULL)
		return OVAL_SCHEMA_VERSION_INVALID;
	sexp_ver = probe_obj_getattrval(obj, "oval_version");

	if (!SEXP_stringp(sexp_ver)) {
		SEXP_free(sexp_ver);
		return OVAL_SCHEMA_VERSION_INVALID;
	}

	char *ver = SEXP_string_cstr(sexp_ver);
	SEXP_free(sexp_ver);
	oval_schema_version_t parsed_version = oval_schema_version_from_cstr(ver);
	free(ver);
	return parsed_version;
}



SEXP_t *probe_obj_getattrval(const SEXP_t * obj, const char *name)
{
	SEXP_t *obj_name;

	obj_name = SEXP_list_first(obj);

	if (SEXP_listp(obj_name)) {
		uint32_t i;
		SEXP_t *attr;
		char *name_buf = NULL;

		i = 2;

		while ((attr = SEXP_list_nth(obj_name, i)) != NULL) {
			if (SEXP_stringp(attr)) {
				if (SEXP_string_nth(attr, 1) == ':') {
					if (name_buf == NULL) {
						name_buf = oscap_sprintf(":%s", name);
					}
					if (SEXP_strcmp(attr, name_buf) == 0) {
						SEXP_t *val;

						val = SEXP_list_nth(obj_name, i + 1);
						free(name_buf);
						SEXP_free(attr);
						SEXP_free(obj_name);

						return (val);
					}

					++i;
				}

				++i;
			}

			SEXP_free(attr);
		}
		free(name_buf);
	}

	SEXP_free(obj_name);

	return (NULL);
}

bool probe_obj_attrexists(const SEXP_t * obj, const char *name)
{
	SEXP_t *obj_name;
	obj_name = SEXP_list_first(obj);

	if (SEXP_listp(obj_name)) {
		uint32_t i;
		SEXP_t *attr;
		char *name_buf = NULL;

		i = 2;

		while ((attr = SEXP_list_nth(obj_name, i)) != NULL) {
			if (SEXP_stringp(attr)) {
				if (SEXP_string_nth(attr, 1) == ':') {
					if (name_buf == NULL) {
						name_buf = oscap_sprintf(":%s", name);
					}
					if (SEXP_strcmp(attr, name_buf) == 0) {
						free(name_buf);
						SEXP_free(attr);
						SEXP_free(obj_name);

						return (true);
					}
					++i;
				} else {
					if (SEXP_strcmp(attr, name) == 0) {
						free(name_buf);
                                        SEXP_free(attr);
                                        SEXP_free(obj_name);
                                        return true;
                                    }
                                }
				++i;
			}

			SEXP_free(attr);
		}
		free(name_buf);
	}

	SEXP_free(obj_name);

	return (false);
}

int probe_obj_setstatus(SEXP_t * obj, oval_syschar_status_t status)
{
        SEXP_t *r0;

	probe_item_attr_add(obj, "status", r0 = SEXP_number_newi_32(status));
        SEXP_free(r0);

	return (-1);
}

char *probe_obj_getname(const SEXP_t * obj)
{
	return probe_ent_getname(obj);
}

size_t probe_obj_getname_r(const SEXP_t * obj, char *buffer, size_t buflen)
{
	return probe_ent_getname_r(obj, buffer, buflen);
}

/*
 * collected objects
 */
SEXP_t *probe_cobj_new(oval_syschar_collection_flag_t flag, SEXP_t *msg_list, SEXP_t *item_list, SEXP_t *mask_list)
{
	SEXP_t *cobj, *r0;

	msg_list = (msg_list == NULL) ? SEXP_list_new(NULL) : SEXP_ref(msg_list);
	item_list = (item_list == NULL) ? SEXP_list_new(NULL) : SEXP_ref(item_list);
        mask_list = mask_list == NULL ? SEXP_list_new(NULL) : SEXP_ref(mask_list);
	cobj = SEXP_list_new(r0 = SEXP_number_newu(flag),
			     msg_list,
			     item_list,
                             mask_list,
			     NULL);
	SEXP_free(msg_list);
	SEXP_free(item_list);
	SEXP_free(mask_list);
	SEXP_free(r0);

	return cobj;
}

int probe_cobj_add_msg(SEXP_t *cobj, const SEXP_t *msg)
{
	SEXP_t *lst;

	lst = SEXP_listref_nth(cobj, 2);
	SEXP_list_add(lst, msg);
	SEXP_free(lst);

	return 0;
}

SEXP_t *probe_cobj_get_msgs(const SEXP_t *cobj)
{
	return SEXP_list_nth(cobj, 2);
}

SEXP_t *probe_cobj_get_mask(const SEXP_t *cobj)
{
    return SEXP_list_nth(cobj, 4);
}

static SEXP_t *probe_item_optimize(const SEXP_t *item);

int probe_cobj_add_item(SEXP_t *cobj, const SEXP_t *item)
{
	SEXP_t *lst, *oitem;

	lst = SEXP_listref_nth(cobj, 3);
	oitem = probe_item_optimize(item);
	SEXP_list_add(lst, oitem);
	SEXP_free(lst);
	SEXP_free(oitem);

	return 0;
}

SEXP_t *probe_cobj_get_items(const SEXP_t *cobj)
{
	return SEXP_list_nth(cobj, 3);
}

void probe_cobj_set_flag(SEXP_t *cobj, oval_syschar_collection_flag_t flag)
{
	SEXP_t *sflag, *old_sflag;
	oval_syschar_collection_flag_t of;

	sflag = SEXP_number_newu(flag);
	old_sflag = SEXP_list_replace(cobj, 1, sflag);
	of = SEXP_number_getu(old_sflag);
	SEXP_free(old_sflag);
	SEXP_free(sflag);
	dD("old flag: %d, new flag: %d.", of, flag);
}

oval_syschar_collection_flag_t probe_cobj_get_flag(const SEXP_t *cobj)
{
	SEXP_t *sflag;
	oval_syschar_collection_flag_t flag;

	sflag = SEXP_list_first(cobj);
	if (sflag == NULL) {
		dE("sflag == NULL.");
		return SYSCHAR_FLAG_UNKNOWN;
	}

	flag = SEXP_number_getu(sflag);
	SEXP_free(sflag);

	return flag;
}

oval_syschar_collection_flag_t probe_cobj_combine_flags(oval_syschar_collection_flag_t f1,
							oval_syschar_collection_flag_t f2,
							oval_setobject_operation_t op)
{
	oval_syschar_collection_flag_t result = SYSCHAR_FLAG_ERROR;

	switch (op) {
	case OVAL_SET_OPERATION_COMPLEMENT:
		if (f1 == SYSCHAR_FLAG_ERROR) {
			result = SYSCHAR_FLAG_ERROR;
		} else if (f1 == SYSCHAR_FLAG_COMPLETE) {
			if (f2 == SYSCHAR_FLAG_NOT_APPLICABLE || f2 == SYSCHAR_FLAG_ERROR) {
				result = SYSCHAR_FLAG_ERROR;
			} else if (f2 == SYSCHAR_FLAG_COMPLETE || f2 == SYSCHAR_FLAG_DOES_NOT_EXIST) {
				result = SYSCHAR_FLAG_COMPLETE;
			} else if (f2 == SYSCHAR_FLAG_INCOMPLETE) {
				result = SYSCHAR_FLAG_ERROR;
			} else if (f2 == SYSCHAR_FLAG_NOT_COLLECTED) {
				result = SYSCHAR_FLAG_ERROR;
			}
		} else if (f1 == SYSCHAR_FLAG_INCOMPLETE) {
			if (f2 == SYSCHAR_FLAG_NOT_APPLICABLE || f2 == SYSCHAR_FLAG_ERROR) {
				result = SYSCHAR_FLAG_ERROR;
			} else if (f2 == SYSCHAR_FLAG_DOES_NOT_EXIST) {
				result = SYSCHAR_FLAG_INCOMPLETE;
			} else if (f2 == SYSCHAR_FLAG_NOT_COLLECTED) {
				result = SYSCHAR_FLAG_ERROR;
			} else if (f2 == SYSCHAR_FLAG_INCOMPLETE) {
				result = SYSCHAR_FLAG_ERROR;
			} else if (f2 == SYSCHAR_FLAG_COMPLETE) {
				result = SYSCHAR_FLAG_INCOMPLETE;
			}
		} else if (f1 == SYSCHAR_FLAG_DOES_NOT_EXIST) {
			if (f2 != SYSCHAR_FLAG_NOT_APPLICABLE) {
				result = SYSCHAR_FLAG_DOES_NOT_EXIST;
			} else {
				result = SYSCHAR_FLAG_ERROR;
			}
		} else if (f1 == SYSCHAR_FLAG_NOT_COLLECTED) {
			if (f2 != SYSCHAR_FLAG_NOT_APPLICABLE && f2 != SYSCHAR_FLAG_ERROR) {
				result = SYSCHAR_FLAG_NOT_COLLECTED;
			} else {
				result = SYSCHAR_FLAG_ERROR;
			}
		} else if (f1 == SYSCHAR_FLAG_NOT_APPLICABLE) {
			result = SYSCHAR_FLAG_ERROR;
		}
		break;

	case OVAL_SET_OPERATION_INTERSECTION:
		if (f1 == SYSCHAR_FLAG_ERROR) {
			result = SYSCHAR_FLAG_ERROR;
		} else if (f1 == SYSCHAR_FLAG_COMPLETE) {
			if (f2 == SYSCHAR_FLAG_ERROR) {
				result = SYSCHAR_FLAG_ERROR;
			} else if (f2 == SYSCHAR_FLAG_DOES_NOT_EXIST) {
				result = SYSCHAR_FLAG_DOES_NOT_EXIST;
			} else if (f2 == SYSCHAR_FLAG_COMPLETE || f2 == SYSCHAR_FLAG_NOT_APPLICABLE) {
				result = SYSCHAR_FLAG_COMPLETE;
			} else if (f2 == SYSCHAR_FLAG_INCOMPLETE) {
				result = SYSCHAR_FLAG_INCOMPLETE;
			} else if (f2 == SYSCHAR_FLAG_NOT_COLLECTED) {
				result = SYSCHAR_FLAG_NOT_COLLECTED;
			}
		} else if (f1 == SYSCHAR_FLAG_INCOMPLETE) {
			if (f2 == SYSCHAR_FLAG_ERROR) {
				result = SYSCHAR_FLAG_ERROR;
			} else if (f2 == SYSCHAR_FLAG_DOES_NOT_EXIST) {
				result = SYSCHAR_FLAG_DOES_NOT_EXIST;
			} else if (f2 == SYSCHAR_FLAG_NOT_APPLICABLE) {
				result = SYSCHAR_FLAG_NOT_APPLICABLE;
			} else if (f2 == SYSCHAR_FLAG_NOT_COLLECTED || f2 == SYSCHAR_FLAG_COMPLETE || f2 == SYSCHAR_FLAG_INCOMPLETE) {
				result = SYSCHAR_FLAG_INCOMPLETE;
			}
		} else if (f1 == SYSCHAR_FLAG_DOES_NOT_EXIST) {
			result = SYSCHAR_FLAG_DOES_NOT_EXIST;
		} else if (f1 == SYSCHAR_FLAG_NOT_COLLECTED) {
			if (f2 == SYSCHAR_FLAG_ERROR) {
				result = SYSCHAR_FLAG_ERROR;
			} else if (f2 == SYSCHAR_FLAG_DOES_NOT_EXIST) {
				result = SYSCHAR_FLAG_DOES_NOT_EXIST;
			} else if (f2 == SYSCHAR_FLAG_COMPLETE || f2 == SYSCHAR_FLAG_INCOMPLETE || f2 == SYSCHAR_FLAG_NOT_APPLICABLE || f2 == SYSCHAR_FLAG_NOT_COLLECTED) {
				result = SYSCHAR_FLAG_NOT_COLLECTED;
			}
		} else if (f1 == SYSCHAR_FLAG_NOT_APPLICABLE) {
			if (f2 == SYSCHAR_FLAG_ERROR) {
				result = SYSCHAR_FLAG_ERROR;
			} else if (f2 == SYSCHAR_FLAG_DOES_NOT_EXIST) {
				result = SYSCHAR_FLAG_DOES_NOT_EXIST;
			} else if (f2 == SYSCHAR_FLAG_COMPLETE) {
				result = SYSCHAR_FLAG_COMPLETE;
			} else if (f2 == SYSCHAR_FLAG_INCOMPLETE) {
				result = SYSCHAR_FLAG_INCOMPLETE;
			} else if (f2 == SYSCHAR_FLAG_NOT_APPLICABLE) {
				result = SYSCHAR_FLAG_NOT_APPLICABLE;
			} else if (f2 == SYSCHAR_FLAG_NOT_COLLECTED) {
				result = SYSCHAR_FLAG_NOT_COLLECTED;
			}
		}
		break;

	case OVAL_SET_OPERATION_UNION:
		if (f1 == SYSCHAR_FLAG_ERROR) {
			result = SYSCHAR_FLAG_ERROR;
		} else if (f1 == SYSCHAR_FLAG_COMPLETE) {
			if (f2 == SYSCHAR_FLAG_ERROR) {
				result = SYSCHAR_FLAG_ERROR;
			} else if (f2 == SYSCHAR_FLAG_COMPLETE || f2 == SYSCHAR_FLAG_DOES_NOT_EXIST || f2 == SYSCHAR_FLAG_NOT_APPLICABLE) {
				result = SYSCHAR_FLAG_COMPLETE;
			} else if (f2 == SYSCHAR_FLAG_INCOMPLETE || f2 == SYSCHAR_FLAG_NOT_COLLECTED) {
				result = SYSCHAR_FLAG_INCOMPLETE;
			}
		} else if (f1 == SYSCHAR_FLAG_INCOMPLETE) {
			if (f2 != SYSCHAR_FLAG_ERROR) {
				result = SYSCHAR_FLAG_INCOMPLETE;
			} else {
				result = SYSCHAR_FLAG_ERROR;
			}
		} else if (f1 == SYSCHAR_FLAG_DOES_NOT_EXIST) {
			if (f2 == SYSCHAR_FLAG_ERROR) {
				result = SYSCHAR_FLAG_ERROR;
			} else if (f2 == SYSCHAR_FLAG_COMPLETE) {
				result = SYSCHAR_FLAG_COMPLETE;
			} else if (f2 == SYSCHAR_FLAG_INCOMPLETE || f2 == SYSCHAR_FLAG_NOT_COLLECTED) {
				result = SYSCHAR_FLAG_INCOMPLETE;
			} else if (f2 == SYSCHAR_FLAG_NOT_APPLICABLE || f2 == SYSCHAR_FLAG_DOES_NOT_EXIST) {
				result = SYSCHAR_FLAG_DOES_NOT_EXIST;
			}
		} else if (f1 == SYSCHAR_FLAG_NOT_COLLECTED) {
			if (f2 == SYSCHAR_FLAG_ERROR) {
				result = SYSCHAR_FLAG_ERROR;
			} else if (f2 == SYSCHAR_FLAG_COMPLETE || f2 == SYSCHAR_FLAG_INCOMPLETE || f2 == SYSCHAR_FLAG_DOES_NOT_EXIST) {
				result = SYSCHAR_FLAG_INCOMPLETE;
			} else if (f2 == SYSCHAR_FLAG_NOT_APPLICABLE || f2 == SYSCHAR_FLAG_NOT_COLLECTED) {
				result = SYSCHAR_FLAG_NOT_COLLECTED;
			}
		} else if (f1 == SYSCHAR_FLAG_NOT_APPLICABLE) {
			if (f2 == SYSCHAR_FLAG_ERROR) {
				result = SYSCHAR_FLAG_ERROR;
			} else if (f2 == SYSCHAR_FLAG_COMPLETE) {
				result = SYSCHAR_FLAG_COMPLETE;
			} else if (f2 == SYSCHAR_FLAG_INCOMPLETE) {
				result = SYSCHAR_FLAG_INCOMPLETE;
			} else if (f2 == SYSCHAR_FLAG_NOT_APPLICABLE) {
				result = SYSCHAR_FLAG_NOT_APPLICABLE;
			} else if (f2 == SYSCHAR_FLAG_DOES_NOT_EXIST) {
				result = SYSCHAR_FLAG_DOES_NOT_EXIST;
			} else if (f2 == SYSCHAR_FLAG_NOT_COLLECTED) {
				result = SYSCHAR_FLAG_NOT_COLLECTED;
			}
		}
		break;

	default:
		result = SYSCHAR_FLAG_ERROR;
	}

	return result;
}

oval_syschar_collection_flag_t probe_cobj_compute_flag(SEXP_t *cobj)
{
	oval_syschar_collection_flag_t flag;
	SEXP_t *items, *item;
	int error_cnt = 0;
	int exists_cnt = 0;
	int does_not_exist_cnt = 0;
	int not_collected_cnt = 0;

	items = probe_cobj_get_items(cobj);
	SEXP_list_foreach(item, items) {
		switch (probe_ent_getstatus(item)) {
		case SYSCHAR_STATUS_ERROR:
			++error_cnt;
			break;
		case SYSCHAR_STATUS_EXISTS:
			++exists_cnt;
			break;
		case SYSCHAR_STATUS_DOES_NOT_EXIST:
			++does_not_exist_cnt;
			break;
		case SYSCHAR_STATUS_NOT_COLLECTED:
			++not_collected_cnt;
			break;
		default:
			SEXP_free(item);
			flag = SYSCHAR_FLAG_ERROR;
			goto cleanup;
		}
	}

	if (error_cnt > 0) {
		flag = SYSCHAR_FLAG_ERROR;
	} else if (not_collected_cnt > 0) {
		flag = SYSCHAR_FLAG_INCOMPLETE;
	} else if (exists_cnt > 0) {
		flag = SYSCHAR_FLAG_COMPLETE;
	} else {
		flag = SYSCHAR_FLAG_DOES_NOT_EXIST;
	}

	if (probe_cobj_get_flag(cobj) == SYSCHAR_FLAG_UNKNOWN)
		probe_cobj_set_flag(cobj, flag);

 cleanup:
	SEXP_free(items);
	return flag;
}

/*
 * messages
 */
SEXP_t *probe_msg_creat(oval_message_level_t level, char *message)
{
	SEXP_t *lvl, *str, *msg;

	dI("%s", message);
	lvl = SEXP_number_newu(level);
	str = SEXP_string_newf("%s", message);
	msg = SEXP_list_new(lvl, str, NULL);
	SEXP_free(lvl);
	SEXP_free(str);

	return msg;
}

SEXP_t *probe_msg_creatf(oval_message_level_t level, const char *fmt, ...)
{
	va_list alist;
	int len = 0;
	SEXP_t *lvl, *str, *msg;
	char *cstr = NULL;

	va_start(alist, fmt);
	len = vsnprintf(cstr, len, fmt, alist);
	va_end(alist);

	if (len < 0) {
		return NULL;
	}

	len++;
	cstr = malloc(len);

	va_start(alist, fmt);
	len = vsnprintf(cstr, len, fmt, alist);
	va_end(alist);

	if (len < 0) {
		free(cstr);
		return NULL;
	}

	dI("%s", cstr);
	str = SEXP_string_new(cstr, len);
	free(cstr);
	lvl = SEXP_number_newu(level);
	msg = SEXP_list_new(lvl, str, NULL);
	SEXP_free(lvl);
	SEXP_free(str);

	return msg;
}

/*
 * entities
 */
SEXP_t *probe_ent_creat(const char *name, SEXP_t * attrs, SEXP_t * val, ...)
{
	va_list ap;
	SEXP_t *ent_list, *ent;

	va_start(ap, val);
	ent_list = SEXP_list_new(NULL);

	while (name != NULL) {
		ent = probe_ent_creat1(name, attrs, val);

		SEXP_list_add(ent_list, ent);
		SEXP_free(ent);

		name = va_arg(ap, const char *);
		attrs = va_arg(ap, SEXP_t *);
		val = va_arg(ap, SEXP_t *);
	}

	va_end(ap);

	return (ent_list);
}

SEXP_t *probe_ent_creat1(const char *name, SEXP_t * attrs, SEXP_t * val)
{
	SEXP_t *ent, *ns;

	ent = SEXP_list_new(NULL);
	ns  = probe_ncache_ref (OSCAP_GSYM(ncache), name);

	if (attrs != NULL) {
		SEXP_t *nl, *nj;

		nl = SEXP_list_new(ns, NULL);
		nj = SEXP_list_join(nl, attrs);

		SEXP_list_add(ent, nj);

		SEXP_free(nl);
		SEXP_free(nj);
	} else
		SEXP_list_add(ent, ns);

	SEXP_free(ns);

	if (val != NULL)
		SEXP_list_add(ent, val);

	return (ent);
}

SEXP_t *probe_ent_attr_add(SEXP_t * ent, const char *name, SEXP_t * val)
{
	return (probe_item_attr_add(ent, name, val));
}

int probe_ent_getvals(const SEXP_t * ent, SEXP_t ** res)
{
	SEXP_t *val_lst;
	int len;

	if (probe_ent_attrexists(ent, "var_ref"))
		val_lst = SEXP_list_nth(ent, 2);
	else
		val_lst = SEXP_list_rest(ent);
	len = SEXP_list_length(val_lst);

	if (res != NULL)
		(*res) = val_lst;
	else
		SEXP_free(val_lst);

	return (len);
}

SEXP_t *probe_ent_getval(const SEXP_t * ent)
{
	if (probe_ent_attrexists(ent, "var_ref")) {
		SEXP_t *r0, *r1;
		unsigned int val_idx;

		r0 = probe_ent_getattrval(ent, "val_idx");
		if (r0 == NULL) {
			r0 = SEXP_list_nth(ent, 2);
			r1 = SEXP_list_first(r0);
		} else {
			val_idx = SEXP_number_getu(r0);
			SEXP_free(r0);

			r0 = SEXP_list_nth(ent, 2);
			r1 = SEXP_list_nth(r0, val_idx + 1);
		}
		SEXP_free(r0);

		return r1;
	} else {
		return (SEXP_list_nth(ent, 2));
	}
}

SEXP_t *probe_ent_getattrval(const SEXP_t * ent, const char *name)
{
	SEXP_t *attrs;

	if (ent == NULL) {
		errno = EFAULT;
		return (NULL);
	}

	attrs = SEXP_list_first(ent);

	if (SEXP_listp(attrs)) {
		SEXP_t *attr;
		uint32_t i;

		i = 2;

		while ((attr = SEXP_list_nth(attrs, i)) != NULL) {
			if (SEXP_stringp(attr)) {
				char attr_name[32 + 1];
				size_t attr_nlen;

				attr_nlen = SEXP_string_cstr_r(attr, attr_name, sizeof attr_name);

				if (attr_nlen > 2) {
					if (attr_name[0] == ':') {
						if (strcmp(attr_name + 1, name) == 0) {
							SEXP_free(attr);
							attr = SEXP_list_nth(attrs, i + 1);
							SEXP_free(attrs);
							return attr;
						}
					}
				}
			}

                        SEXP_free(attr);
			++i;
		}
	}

	SEXP_free(attrs);
	return (NULL);
}

bool probe_ent_attrexists(const SEXP_t * ent, const char *name)
{
	return probe_obj_attrexists(ent, name);
}

int probe_ent_setdatatype(SEXP_t *ent, oval_datatype_t type)
{
	const char *strtype;

	_A(ent != NULL);

	switch (type) {
	case OVAL_DATATYPE_BOOLEAN:
	case OVAL_DATATYPE_FLOAT:
	case OVAL_DATATYPE_INTEGER:
	case OVAL_DATATYPE_STRING:
		/* these are stored directly in each SEXP */
		return 0;
	default:
		/* set the SEXP to a user datatype */
		strtype = oval_datatype_get_text(type);
		return SEXP_datatype_set(ent, strtype);
	}
}

static oval_datatype_t _sexp_val_getdatatype(const SEXP_t *val)
{
	SEXP_type_t sdt;
	SEXP_numtype_t sndt;

	sdt = SEXP_typeof(val);

	switch (sdt) {
	case SEXP_TYPE_STRING:
		return OVAL_DATATYPE_STRING;
	case SEXP_TYPE_NUMBER:
		sndt = SEXP_number_type(val);
		switch (sndt) {
		case SEXP_NUM_BOOL:
			return OVAL_DATATYPE_BOOLEAN;
		case SEXP_NUM_FLOAT:
			return OVAL_DATATYPE_FLOAT;
		default: /* everything else is considered an integer */
			return OVAL_DATATYPE_INTEGER;
		}
	default:
		dE("Unexpected SEXP datatype: %d, '%s'.", sdt, SEXP_strtype(val));
		return OVAL_DATATYPE_UNKNOWN;
	}
}

oval_datatype_t probe_ent_getdatatype(const SEXP_t * ent)
{
	const char *str;

	_A(ent != NULL);

	str = SEXP_datatype(ent);
	if (str != NULL) {
		/* user datatype */
		return oval_datatype_from_text(str);
	} else {
		/* SEXP datatype */
		SEXP_t *val;
		oval_datatype_t dt;

		val = probe_ent_getval(ent);
		if (val == NULL)
			return OVAL_DATATYPE_UNKNOWN;

		dt = _sexp_val_getdatatype(val);
		SEXP_free(val);

		return dt;
	}
}

int probe_ent_setmask(SEXP_t * ent, bool mask)
{
	/* TBI */
	return (-1);
}

bool probe_ent_getmask(const SEXP_t * ent)
{
	/* TBI */
	return (false);
}

int probe_ent_setstatus(SEXP_t * ent, oval_syschar_status_t status)
{
        SEXP_t *r0;

        probe_item_attr_add (ent, "status", r0 = SEXP_number_newi_32(status));
        SEXP_free(r0);

	return (0);
}

oval_syschar_status_t probe_ent_getstatus(const SEXP_t * ent)
{
        SEXP_t *val;
        oval_syschar_status_t sta;

        val = probe_ent_getattrval (ent, "status");

        if (val != NULL) {
                sta = (oval_syschar_status_t) SEXP_number_geti_32 (val);
                SEXP_free (val);
        } else {
                sta = SYSCHAR_STATUS_EXISTS;
        }

	return (sta);
}

char *probe_ent_getname(const SEXP_t * ent)
{
	SEXP_t *ent_name;
	char *name_str;

	if (ent == NULL) {
		errno = EFAULT;
		return (NULL);
	}

	name_str = NULL;
	ent_name = SEXP_list_first(ent);

	if (ent_name == NULL) {
		errno = EINVAL;
		return (NULL);
	}

	SEXP_type_t sexp_type = SEXP_typeof(ent_name);
	if (sexp_type == SEXP_TYPE_LIST) {
		SEXP_t *tmp;

		tmp = SEXP_list_first(ent_name);
		SEXP_free(ent_name);
		ent_name = tmp;

		if (!SEXP_stringp(ent_name)) {
			SEXP_free(ent_name);
			errno = EINVAL;
			return NULL;
		}
	}
	if (sexp_type == SEXP_TYPE_LIST || sexp_type == SEXP_TYPE_STRING) {
		if (SEXP_string_length(ent_name) > 0)
			name_str = SEXP_string_cstr(ent_name);
		else
			errno = EINVAL;
	}

	SEXP_free(ent_name);

	return (name_str);
}

size_t probe_ent_getname_r(const SEXP_t * ent, char *buffer, size_t buflen)
{
	SEXP_t *ent_name;
	size_t name_len;

	if (ent == NULL) {
		errno = EFAULT;
		return (0);
	}

	name_len = 0;
	ent_name = SEXP_list_first(ent);

	if (ent_name == NULL) {
		errno = EINVAL;
		return (0);
	}

	SEXP_type_t sexp_type = SEXP_typeof(ent_name);
	if (sexp_type == SEXP_TYPE_LIST) {
		SEXP_t *tmp;

		tmp = SEXP_list_first(ent_name);
		SEXP_free(ent_name);
		ent_name = tmp;

		if (!SEXP_stringp(ent_name)) {
			SEXP_free(ent_name);
			errno = EINVAL;
			return name_len;
		}
	}
	if (sexp_type == SEXP_TYPE_LIST || sexp_type == SEXP_TYPE_STRING) {
		if (SEXP_string_length(ent_name) > 0)
			name_len = SEXP_string_cstr_r(ent_name, buffer, buflen);
		else
			errno = EINVAL;
	}

	SEXP_free(ent_name);

	return (name_len);
}

void probe_free(SEXP_t * obj)
{
	SEXP_free(obj);
}

void probe_filebehaviors_canonicalize(SEXP_t **behaviors)
{
	SEXP_t *bhs, *r0;

	_A(behaviors != NULL);

	bhs = *behaviors;

	if (!bhs) {
		r0 = SEXP_list_new(NULL);
		bhs = *behaviors = probe_ent_creat1("behaviors", r0, NULL);
		SEXP_free(r0);
	}

	if (!probe_ent_attrexists(bhs, "max_depth")) {
		r0 = SEXP_string_newf("-1");
		probe_ent_attr_add(bhs, "max_depth", r0);
		SEXP_free(r0);
	}
	if (!probe_ent_attrexists(bhs, "recurse")) {
		r0 = SEXP_string_newf("symlinks and directories");
		probe_ent_attr_add(bhs, "recurse", r0);
		SEXP_free(r0);
	}
	if (!probe_ent_attrexists(bhs, "recurse_direction")) {
		r0 = SEXP_string_newf("none");
		probe_ent_attr_add(bhs, "recurse_direction", r0);
		SEXP_free(r0);
	}
	if (!probe_ent_attrexists(bhs, "recurse_file_system")) {
		r0 = SEXP_string_newf("all");
		probe_ent_attr_add(bhs, "recurse_file_system", r0);
		SEXP_free(r0);
	}
}

void probe_tfc54behaviors_canonicalize(SEXP_t **behaviors)
{
	SEXP_t *bhs, *r0;

	_A(behaviors != NULL);

	bhs = *behaviors;

	if (!bhs) {
		r0 = SEXP_list_new(NULL);
		bhs = *behaviors = probe_ent_creat1("behaviors", r0, NULL);
		SEXP_free(r0);
	}

	probe_filebehaviors_canonicalize(&bhs);

	if (!probe_ent_attrexists(bhs, "ignore_case")) {
		r0 = SEXP_string_newf("0");
		probe_ent_attr_add(bhs, "ignore_case", r0);
		SEXP_free(r0);
	}
	if (!probe_ent_attrexists(bhs, "multiline")) {
		r0 = SEXP_string_newf("1");
		probe_ent_attr_add(bhs, "multiline", r0);
		SEXP_free(r0);
	}
	if (!probe_ent_attrexists(bhs, "singleline")) {
		r0 = SEXP_string_newf("0");
		probe_ent_attr_add(bhs, "singleline", r0);
		SEXP_free(r0);
	}
}

/**
 * Return a copy of the supplied item with optimized memory representation
 */
static SEXP_t *probe_item_optimize(const SEXP_t *item)
{
	// todo
	return SEXP_ref(item);
}

/**
 * The order of (value_name, value_type, *value) argument tuples passed as
 * e.g. 3rd to 5th arguments matters. If you change ordering of those tuples,
 * it will have consequences.
 */
SEXP_t *probe_item_create(oval_subtype_t item_subtype, probe_elmatr_t *item_attributes[],
                          /* const char *value_name, oval_datatype_t value_type, void *value, */ ...)
{
        va_list ap;
	SEXP_t *item, *name_sexp, *value_sexp = NULL, *entity;
        SEXP_t value_sexp_mem, entity_mem;
	const char *value_name, *subtype_name, *family_name;
	oval_family_t family;
	char item_name[128];
        oval_datatype_t value_type;

        char   *value_str, **value_stra;
        int64_t value_int;
        double  value_flt;
        bool    value_bool;
        bool    free_value = true;
        bool    multiplied;
        int     value_i, multiply;

        subtype_name = oval_subtype_to_str(item_subtype);

        if (subtype_name == NULL) {
                dE("Invalid/Unknown subtype: %d", (int)item_subtype);
                return (NULL);
        }

	family = oval_subtype_get_family(item_subtype);
	family_name = oval_family_get_text(family);
	snprintf(item_name, sizeof(item_name), "%s:%s_item", family_name, subtype_name);

	va_start(ap, item_attributes);

	item       = probe_item_new(item_name, NULL);
	value_name = va_arg(ap, const char *);

        while (value_name != NULL) {
                value_i    = 0;
                multiply   = 1;
                multiplied = false;
		value_type = va_arg(ap, oval_datatype_t);

                switch (value_type) {
                case OVAL_DATATYPE_STRING:
                        value_str  = va_arg(ap, char *);

                        if (value_str == NULL)
                                goto skip;

                        value_sexp = SEXP_string_new_r(&value_sexp_mem, value_str, strlen(value_str));
                        break;
                case OVAL_DATATYPE_STRING_M:
                        value_type = OVAL_DATATYPE_STRING;
                        value_stra = va_arg(ap, char **);
                        multiplied = true;
                        multiply   = 0;

                        if (value_stra == NULL)
                                goto skip;

                        while (value_stra[multiply] != NULL)
                                ++multiply;

                        value_sexp = malloc(sizeof(SEXP_t) * multiply);

                        for (value_i = 0; value_i < multiply; ++value_i)
                                SEXP_string_new_r(value_sexp + value_i, value_stra[value_i], strlen(value_stra[value_i]));

                        value_i = 0;
                        break;
                case OVAL_DATATYPE_BOOLEAN:
                        value_bool = (bool)va_arg(ap, int);
                        value_sexp = SEXP_number_newb_r(&value_sexp_mem, value_bool);
                        break;
                case OVAL_DATATYPE_INTEGER:
                        value_int  = va_arg(ap, int64_t);
                        value_sexp = SEXP_number_newi_64_r(&value_sexp_mem, value_int);
                        break;
                case OVAL_DATATYPE_FLOAT:
                        value_flt  = va_arg(ap, double);
                        value_sexp = SEXP_number_newf_r(&value_sexp_mem, value_flt);
                        break;
                case OVAL_DATATYPE_SEXP:
                        value_sexp = va_arg(ap, SEXP_t *);

                        if (value_sexp == NULL)
                                goto skip;
                        else
                                free_value = false;

			value_type = _sexp_val_getdatatype(value_sexp);
                        break;
                case OVAL_DATATYPE_RECORD:
			entity = va_arg(ap, SEXP_t *);
			SEXP_list_add(item, entity);
			goto skip;
                case OVAL_DATATYPE_EVR_STRING:
                case OVAL_DATATYPE_DEBIAN_EVR_STRING:
                case OVAL_DATATYPE_FILESET_REVISION:
                case OVAL_DATATYPE_IOS_VERSION:
		case OVAL_DATATYPE_IPV4ADDR:
		case OVAL_DATATYPE_IPV6ADDR:
                case OVAL_DATATYPE_VERSION:
                        value_str  = va_arg(ap, char *);
                        value_sexp = SEXP_string_new_r(&value_sexp_mem, value_str, strlen(value_str));

                        break;
                        /* TODO */
                case OVAL_DATATYPE_BINARY:
                case OVAL_DATATYPE_UNKNOWN:
			dE("Unknown or unsupported OVAL datatype: %d, '%s', name: '%s'.",
			   value_type, oval_datatype_get_text(value_type), value_name);
                        SEXP_free(item);

			va_end(ap);
                        return (NULL);
                }

                name_sexp = probe_ncache_ref(OSCAP_GSYM(ncache), value_name);

                while(value_i < multiply) {
                        entity = SEXP_list_new_r(&entity_mem, name_sexp, value_sexp + value_i, NULL);

                        if (probe_ent_setdatatype(entity, value_type) != 0) {
                                SEXP_free_r(&entity_mem);
                                SEXP_free(name_sexp);
                                SEXP_free(item);

                                if (free_value) {
                                        while(value_i < multiply)
                                                SEXP_free_r(value_sexp + value_i++);
                                        if (multiplied)
                                                free(value_sexp);
                                }

				va_end(ap);
                                return (NULL);
                        }

			if (item == NULL || entity == NULL || name_sexp == NULL) {
				return NULL;
			}
                        SEXP_list_add(item, entity);
                        SEXP_free_r(&entity_mem);

                        if (free_value)
                                SEXP_free_r(value_sexp + value_i);
                        ++value_i;
                }

                SEXP_free(name_sexp);

                if (multiplied)
                        free(value_sexp);
        skip:
                value_name = va_arg(ap, const char *);
		free_value = true;
        }

	va_end(ap);
        return (item);
}

oval_operation_t probe_ent_getoperation(SEXP_t *entity, oval_operation_t default_op)
{
        oval_operation_t ret;
        SEXP_t *aval;

        if (entity == NULL) {
                dW("entity == NULL");
                return (OVAL_OPERATION_UNKNOWN);
        }

        aval = probe_ent_getattrval(entity, "operation");

        if (aval == NULL) {
                dW("Attribute \"operation\" not found. Using default.");
                return (default_op);
        }

        if (!SEXP_numberp(aval)) {
                dW("Invalid type");
                SEXP_free(aval);
                return (OVAL_OPERATION_UNKNOWN);
        }

        ret = SEXP_number_geti_32(aval);
        SEXP_free(aval);

        return (ret);
}

int probe_item_add_msg(SEXP_t *item, oval_message_level_t msglvl, char *msgfmt, ...)
{
    va_list ap;
    SEXP_t  lvl_sexp, msg_sexp;
    SEXP_t *attrs;
    char    msg_buffer[1024];
    int     msg_length;

    va_start(ap, msgfmt);

    msg_length = vsnprintf(msg_buffer, sizeof msg_buffer, msgfmt, ap);

    if (msg_length < 0) {
	dE("vsnprintf failed! errno=%u, %s.", errno, strerror(errno));
	va_end(ap);
	return (-1);
    }

    if ((size_t)msg_length >= sizeof msg_buffer) {
	dE("message too long!");
	va_end(ap);
	return (-1);
    }

    va_end(ap);

    SEXP_number_newu_32_r(&lvl_sexp, msglvl);
    SEXP_string_new_r(&msg_sexp, msg_buffer, msg_length);

    attrs = probe_attr_creat("level", &lvl_sexp, NULL);
    probe_item_ent_add(item, "message", attrs, &msg_sexp);

    SEXP_free_r(&lvl_sexp);
    SEXP_free_r(&msg_sexp);
    SEXP_free(attrs);

    return (0);
}

SEXP_t *probe_entval_from_cstr(oval_datatype_t type,
                               const char *value, size_t vallen)
{
  SEXP_t *ent_val = NULL;

  if (value == NULL || vallen == 0)
    return NULL;

	switch (type) {
	case OVAL_DATATYPE_FLOAT:
	{
		double val;
		char *end = NULL;

		val = strto_double(value, vallen, &end);
		ent_val = SEXP_number_newf(val);

	}	break;
	case OVAL_DATATYPE_INTEGER:
	{
		int64_t val;
		char *end;

		val = strto_int64(value, vallen, &end, 10);
		ent_val = SEXP_number_newi_64(val);

	}	break;
	case OVAL_DATATYPE_BOOLEAN:
		switch(vallen) {
		case 1:
			switch(*value)
			{
			case '1':
				ent_val = SEXP_number_newb(true);
				break;
			case '0':
				ent_val = SEXP_number_newb(false);
				break;
			}
			break;
		case 4:
			if (oscap_strncasecmp(value, "true", 4) == 0)
				ent_val = SEXP_number_newb(true);
			break;
		case 5:
			if (oscap_strncasecmp(value, "false", 5) == 0)
				ent_val = SEXP_number_newb(false);
			break;
		}

		if (ent_val == NULL)
			return NULL;
		break;


	case OVAL_DATATYPE_IPV4ADDR:
	{
		struct in_addr ip4;

		if (inet_pton(AF_INET, value, &ip4) != 1)
			return NULL;
	}	break;
	case OVAL_DATATYPE_IPV6ADDR:
	{
		struct in6_addr ip6;

		if (inet_pton(AF_INET6, value, &ip6) != 1)
			return NULL;
	}	break;

	case OVAL_DATATYPE_EVR_STRING:
	case OVAL_DATATYPE_DEBIAN_EVR_STRING:
	case OVAL_DATATYPE_FILESET_REVISION:
	case OVAL_DATATYPE_IOS_VERSION:
	case OVAL_DATATYPE_STRING:
	case OVAL_DATATYPE_VERSION:
		break;

	case OVAL_DATATYPE_BINARY:
	case OVAL_DATATYPE_SEXP:
	case OVAL_DATATYPE_UNKNOWN:
	case OVAL_DATATYPE_STRING_M:
	case OVAL_DATATYPE_RECORD:
	default:
		return NULL;
	}

	/*
	 * If we got here and ent_val is still NULL, then
	 * no special conversion procedure is needed and
	 * we can simply create an SEXP string...
	 */
	if (ent_val == NULL)
		ent_val = SEXP_string_new(value, vallen);

  return ent_val;
}

SEXP_t *probe_ent_from_cstr(const char *name, oval_datatype_t type,
                            const char *value, size_t vallen)
{
	SEXP_t *ent = NULL, *ent_val = NULL;

	if (name == NULL || value == NULL || vallen == 0)
		return NULL;

  ent_val = probe_entval_from_cstr(type, value, vallen);

  if (ent_val == NULL)
    return NULL;
  
	/* Create the entity... */
	ent = probe_ent_creat1(name, NULL, ent_val);	
	SEXP_free(ent_val);

	/* ...and annotate with the specified OVAL datatype */
	if (probe_ent_setdatatype(ent, type) != 0) {
		SEXP_free(ent);
		return NULL;
	}

	return ent;
}

SEXP_t *probe_obj_getmask(SEXP_t *obj)
{
    SEXP_t *objents, *ent, *ent_name, *mask;

    SEXP_VALIDATE(obj);

    if (obj == NULL)
       return NULL;

    ent     = NULL;
    mask    = SEXP_list_new(NULL);
    objents = SEXP_list_rest(obj);

    SEXP_list_foreach(ent, objents) {
        ent_name = SEXP_list_first(ent);

        if (SEXP_listp(ent_name)) {
            SEXP_t *nr;

            nr = SEXP_list_first(ent_name);
            SEXP_free(ent_name);
            ent_name = nr;

            if (probe_ent_attrexists(ent, "mask"))
                SEXP_list_add(mask, ent_name);
        }
        SEXP_free(ent_name);
    }

    SEXP_free(objents);
    return (mask);
}
/// @}
