/**
 *  @file
 *  Implementation of seaudit filters private functions.
 *
 *  @author Jeremy A. Mowery jmowery@tresys.com
 *  @author Jason Tang jtang@tresys.com
 *  @author Jeremy Solt jsolt@tresys.com
 *
 *  Copyright (C) 2004-2007 Tresys Technology, LLC
 *
 *  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 St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "seaudit_internal.h"
#include "filter-internal.h"

#include <apol/util.h>

#include <errno.h>
#include <fnmatch.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <libxml/uri.h>

/******************** support functions ********************/

static int filter_string_vector_read(apol_vector_t ** v, const xmlChar * ch)
{
	char *s;
	if (*v == NULL && (*v = apol_vector_create_with_capacity(1, free)) == NULL) {
		return -1;
	}
	if ((s = xmlURIUnescapeString((const char *)ch, 0, NULL)) == NULL || apol_vector_append(*v, s) < 0) {
		free(s);
		return -1;
	}
	return 0;
}

static int filter_string_read(char **dest, const xmlChar * ch)
{
	free(*dest);
	*dest = NULL;
	if ((*dest = xmlURIUnescapeString((const char *)ch, 0, NULL)) == NULL) {
		return -1;
	}
	return 0;
}

static int filter_ulong_read(unsigned long *dest, const xmlChar * ch)
{
	char *s, *endptr;
	int retval = -1;
	if ((s = xmlURIUnescapeString((const char *)ch, 0, NULL)) == NULL) {
		return -1;
	}
	*dest = strtoul(s, &endptr, 10);
	if (*s != '\0' && *endptr == '\0') {
		retval = 0;
	}
	free(s);
	return retval;
}

static unsigned int filter_uint_read(unsigned int *dest, const xmlChar * ch)
{
	char *s, *endptr;
	int retval = -1;
	if ((s = xmlURIUnescapeString((const char *)ch, 0, NULL)) == NULL) {
		return -1;
	}
	*dest = (unsigned int)(strtoul(s, &endptr, 10));
	if (*s != '\0' && *endptr == '\0') {
		retval = 0;
	}
	free(s);
	return retval;
}

static int filter_int_read(int *dest, const xmlChar * ch)
{
	char *s, *endptr;
	int retval = -1;
	if ((s = xmlURIUnescapeString((const char *)ch, 0, NULL)) == NULL) {
		return -1;
	}
	*dest = (int)(strtol(s, &endptr, 10));
	if (*s != '\0' && *endptr == '\0') {
		retval = 0;
	}
	free(s);
	return retval;
}

static void filter_string_vector_print(const char *criteria_name, apol_vector_t * v, FILE * f, int tabs)
{
	int i;
	size_t j;
	if (v == NULL) {
		return;
	}
	for (i = 0; i < tabs; i++)
		fprintf(f, "\t");
	fprintf(f, "<criteria type=\"%s\">\n", criteria_name);
	for (j = 0; j < apol_vector_get_size(v); j++) {
		xmlChar *s = xmlCharStrdup(apol_vector_get_element(v, j));
		xmlChar *escaped = xmlURIEscapeStr(s, NULL);
		for (i = 0; i < tabs + 1; i++) {
			fprintf(f, "\t");
		}
		fprintf(f, "<item>%s</item>\n", escaped);
		free(escaped);
		free(s);
	}
	for (i = 0; i < tabs; i++)
		fprintf(f, "\t");
	fprintf(f, "</criteria>\n");
}

static void filter_string_print(const char *criteria_name, const char *s, FILE * f, int tabs)
{
	int i;
	xmlChar *t, *escaped;
	if (s == NULL) {
		return;
	}
	t = xmlCharStrdup(s);
	escaped = xmlURIEscapeStr(t, NULL);
	for (i = 0; i < tabs; i++)
		fprintf(f, "\t");
	fprintf(f, "<criteria type=\"%s\">\n", criteria_name);
	for (i = 0; i < tabs + 1; i++) {
		fprintf(f, "\t");
	}
	fprintf(f, "<item>%s</item>\n", escaped);
	for (i = 0; i < tabs; i++)
		fprintf(f, "\t");
	fprintf(f, "</criteria>\n");
	free(escaped);
	free(t);
}

static void filter_ulong_print(const char *criteria_name, const unsigned long val, FILE * f, int tabs)
{
	int i;
	for (i = 0; i < tabs; i++)
		fprintf(f, "\t");
	fprintf(f, "<criteria type=\"%s\">\n", criteria_name);
	for (i = 0; i < tabs + 1; i++) {
		fprintf(f, "\t");
	}
	fprintf(f, "<item>%lu</item>\n", val);
	for (i = 0; i < tabs; i++)
		fprintf(f, "\t");
	fprintf(f, "</criteria>\n");
}

static void filter_uint_print(const char *criteria_name, const unsigned int val, FILE * f, int tabs)
{
	int i;
	for (i = 0; i < tabs; i++)
		fprintf(f, "\t");
	fprintf(f, "<criteria type=\"%s\">\n", criteria_name);
	for (i = 0; i < tabs + 1; i++) {
		fprintf(f, "\t");
	}
	fprintf(f, "<item>%u</item>\n", val);
	for (i = 0; i < tabs; i++)
		fprintf(f, "\t");
	fprintf(f, "</criteria>\n");
}

static void filter_int_print(const char *criteria_name, const int val, FILE * f, int tabs)
{
	int i;
	for (i = 0; i < tabs; i++)
		fprintf(f, "\t");
	fprintf(f, "<criteria type=\"%s\">\n", criteria_name);
	for (i = 0; i < tabs + 1; i++) {
		fprintf(f, "\t");
	}
	fprintf(f, "<item>%d</item>\n", val);
	for (i = 0; i < tabs; i++)
		fprintf(f, "\t");
	fprintf(f, "</criteria>\n");
}

/******************** filter private functions ********************/

static bool filter_src_user_is_set(const seaudit_filter_t * filter)
{
	return filter->src_users != NULL;
}

static int filter_src_user_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->suser != NULL;
}

static int filter_src_user_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	size_t i;
	return apol_vector_get_index(filter->src_users, msg->data.avc->suser, apol_str_strcmp, NULL, &i) == 0;
}

static int filter_src_user_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_vector_read(&filter->src_users, ch);
}

static void filter_src_user_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_vector_print(name, filter->src_users, f, tabs);
}

static bool filter_src_role_is_set(const seaudit_filter_t * filter)
{
	return filter->src_roles != NULL;
}

static int filter_src_role_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->srole != NULL;
}

static int filter_src_role_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	size_t i;
	return apol_vector_get_index(filter->src_roles, msg->data.avc->srole, apol_str_strcmp, NULL, &i) == 0;
}

static int filter_src_role_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_vector_read(&filter->src_roles, ch);
}

static void filter_src_role_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_vector_print(name, filter->src_roles, f, tabs);
}

static bool filter_src_type_is_set(const seaudit_filter_t * filter)
{
	return filter->src_types != NULL;
}

static int filter_src_type_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->stype != NULL;
}

static int filter_src_type_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_vector_read(&filter->src_types, ch);
}

static int filter_src_type_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	size_t i;
	return apol_vector_get_index(filter->src_types, msg->data.avc->stype, apol_str_strcmp, NULL, &i) == 0;
}

static void filter_src_type_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_vector_print(name, filter->src_types, f, tabs);
}

static bool filter_src_mls_lvl_is_set(const seaudit_filter_t * filter)
{
	return filter->src_mls_lvl != NULL;
}

static int filter_src_mls_lvl_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->smls_lvl != NULL;
}

static int filter_src_mls_lvl_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_vector_read(&filter->src_mls_lvl, ch);
}

static int filter_src_mls_lvl_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	size_t i;
	return apol_vector_get_index(filter->src_mls_lvl, msg->data.avc->smls_lvl, apol_str_strcmp, NULL, &i) == 0;
}

static void filter_src_mls_lvl_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_vector_print(name, filter->src_mls_lvl, f, tabs);
}

static bool filter_src_mls_clr_is_set(const seaudit_filter_t * filter)
{
	return filter->src_mls_clr != NULL;
}

static int filter_src_mls_clr_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->smls_clr != NULL;
}

static int filter_src_mls_clr_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_vector_read(&filter->src_mls_clr, ch);
}

static int filter_src_mls_clr_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	size_t i;
	return apol_vector_get_index(filter->src_mls_clr, msg->data.avc->smls_clr, apol_str_strcmp, NULL, &i) == 0;
}

static void filter_src_mls_clr_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_vector_print(name, filter->src_mls_clr, f, tabs);
}

static bool filter_tgt_user_is_set(const seaudit_filter_t * filter)
{
	return filter->tgt_users != NULL;
}

static int filter_tgt_user_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->tuser != NULL;
}

static int filter_tgt_user_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	size_t i;
	return apol_vector_get_index(filter->tgt_users, msg->data.avc->tuser, apol_str_strcmp, NULL, &i) == 0;
}

static int filter_tgt_user_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_vector_read(&filter->tgt_users, ch);
}

static void filter_tgt_user_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_vector_print(name, filter->tgt_users, f, tabs);
}

static bool filter_tgt_role_is_set(const seaudit_filter_t * filter)
{
	return filter->tgt_roles != NULL;
}

static int filter_tgt_role_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->trole != NULL;
}

static int filter_tgt_role_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	size_t i;
	return apol_vector_get_index(filter->tgt_roles, msg->data.avc->trole, apol_str_strcmp, NULL, &i) == 0;
}

static int filter_tgt_role_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_vector_read(&filter->tgt_roles, ch);
}

static void filter_tgt_role_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_vector_print(name, filter->tgt_roles, f, tabs);
}

static bool filter_tgt_type_is_set(const seaudit_filter_t * filter)
{
	return filter->tgt_types != NULL;
}

static int filter_tgt_type_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->ttype != NULL;
}

static int filter_tgt_type_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	size_t i;
	return apol_vector_get_index(filter->tgt_types, msg->data.avc->ttype, apol_str_strcmp, NULL, &i) == 0;
}

static int filter_tgt_type_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_vector_read(&filter->tgt_types, ch);
}

static void filter_tgt_type_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_vector_print(name, filter->tgt_types, f, tabs);
}

static bool filter_tgt_mls_lvl_is_set(const seaudit_filter_t * filter)
{
	return filter->tgt_mls_lvl != NULL;
}

static int filter_tgt_mls_lvl_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->tmls_lvl != NULL;
}

static int filter_tgt_mls_lvl_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_vector_read(&filter->tgt_mls_lvl, ch);
}

static int filter_tgt_mls_lvl_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	size_t i;
	return apol_vector_get_index(filter->tgt_mls_lvl, msg->data.avc->tmls_lvl, apol_str_strcmp, NULL, &i) == 0;
}

static void filter_tgt_mls_lvl_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_vector_print(name, filter->tgt_mls_lvl, f, tabs);
}

static bool filter_tgt_mls_clr_is_set(const seaudit_filter_t * filter)
{
	return filter->tgt_mls_clr != NULL;
}

static int filter_tgt_mls_clr_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->tmls_clr != NULL;
}

static int filter_tgt_mls_clr_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_vector_read(&filter->tgt_mls_clr, ch);
}

static int filter_tgt_mls_clr_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	size_t i;
	return apol_vector_get_index(filter->tgt_mls_clr, msg->data.avc->tmls_clr, apol_str_strcmp, NULL, &i) == 0;
}

static void filter_tgt_mls_clr_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_vector_print(name, filter->tgt_mls_clr, f, tabs);
}


static bool filter_tgt_class_is_set(const seaudit_filter_t * filter)
{
	return filter->tgt_classes != NULL;
}

static int filter_tgt_class_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->tclass != NULL;
}

static int filter_tgt_class_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	size_t i;
	return apol_vector_get_index(filter->tgt_classes, msg->data.avc->tclass, apol_str_strcmp, NULL, &i) == 0;
}

static int filter_tgt_class_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_vector_read(&filter->tgt_classes, ch);
}

static void filter_tgt_class_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_vector_print(name, filter->tgt_classes, f, tabs);
}

static bool filter_perm_is_set(const seaudit_filter_t * filter)
{
	return filter->perm != NULL;
}

static int filter_perm_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->perms != NULL &&
		apol_vector_get_size(msg->data.avc->perms) >= 1;
}

static int filter_perm_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	size_t i;
	for (i = 0; i < apol_vector_get_size(msg->data.avc->perms); i++) {
		const char *p = apol_vector_get_element(msg->data.avc->perms, i);
		if (fnmatch(filter->perm, p, 0) == 0) {
			return 1;
		}
	}
	return 0;
}

static int filter_perm_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_read(&filter->perm, ch);
}

static void filter_perm_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_print(name, filter->perm, f, tabs);
}

static bool filter_exe_is_set(const seaudit_filter_t * filter)
{
	return filter->exe != NULL;
}

static int filter_exe_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->exe != NULL;
}

static int filter_exe_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return fnmatch(filter->exe, msg->data.avc->exe, 0) == 0;
}

static int filter_exe_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_read(&filter->exe, ch);
}

static void filter_exe_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_print(name, filter->exe, f, tabs);
}

static bool filter_host_is_set(const seaudit_filter_t * filter)
{
	return filter->host != NULL;
}

static int filter_host_support(const seaudit_message_t * msg)
{
	return msg->host != NULL;
}

static int filter_host_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return fnmatch(filter->host, msg->host, 0) == 0;
}

static int filter_host_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_read(&filter->host, ch);
}

static void filter_host_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_print(name, filter->host, f, tabs);
}

static bool filter_path_is_set(const seaudit_filter_t * filter)
{
	return filter->path != NULL;
}

static int filter_path_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->path != NULL;
}

static int filter_path_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return fnmatch(filter->path, msg->data.avc->path, 0) == 0;
}

static int filter_path_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_read(&filter->path, ch);
}

static void filter_path_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_print(name, filter->path, f, tabs);
}

static bool filter_inode_is_set(const seaudit_filter_t * filter)
{
	return filter->inode != 0;
}

static int filter_inode_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->is_inode;
}

static int filter_inode_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return filter->inode == msg->data.avc->inode;
}

static int filter_inode_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_ulong_read(&filter->inode, ch);
}

static void filter_inode_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_ulong_print(name, filter->inode, f, tabs);
}

static bool filter_pid_is_set(const seaudit_filter_t * filter)
{
	return filter->pid != 0;
}

static int filter_pid_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->is_pid;
}

static int filter_pid_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return filter->pid == msg->data.avc->pid;
}

static int filter_pid_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_uint_read(&filter->pid, ch);
}

static void filter_pid_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_uint_print(name, filter->pid, f, tabs);
}

static bool filter_comm_is_set(const seaudit_filter_t * filter)
{
	return filter->comm != NULL;
}

static int filter_comm_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->comm != NULL;
}

static int filter_comm_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return fnmatch(filter->comm, msg->data.avc->comm, 0) == 0;
}

static int filter_comm_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_read(&filter->comm, ch);
}

static void filter_comm_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_print(name, filter->comm, f, tabs);
}

static bool filter_anyaddr_is_set(const seaudit_filter_t * filter)
{
	return filter->anyaddr != NULL;
}

static int filter_anyaddr_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && (msg->data.avc->saddr != NULL
							 || msg->data.avc->daddr != NULL
							 || msg->data.avc->faddr != NULL
							 || msg->data.avc->laddr != NULL || msg->data.avc->ipaddr != NULL);
}

static int filter_anyaddr_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	if (msg->data.avc->saddr && fnmatch(filter->anyaddr, msg->data.avc->saddr, 0) == 0)
		return 1;
	if (msg->data.avc->daddr && fnmatch(filter->anyaddr, msg->data.avc->daddr, 0) == 0)
		return 1;
	if (msg->data.avc->faddr && fnmatch(filter->anyaddr, msg->data.avc->faddr, 0) == 0)
		return 1;
	if (msg->data.avc->laddr && fnmatch(filter->anyaddr, msg->data.avc->laddr, 0) == 0)
		return 1;
	if (msg->data.avc->ipaddr && fnmatch(filter->anyaddr, msg->data.avc->ipaddr, 0) == 0)
		return 1;
	return 0;
}

static int filter_anyaddr_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_read(&filter->anyaddr, ch);
}

static void filter_anyaddr_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_print(name, filter->anyaddr, f, tabs);
}

static bool filter_anyport_is_set(const seaudit_filter_t * filter)
{
	return filter->anyport != 0;
}

static int filter_anyport_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && (msg->data.avc->port != 0 ||
							 msg->data.avc->source != 0 ||
							 msg->data.avc->dest != 0 ||
							 msg->data.avc->fport != 0 || msg->data.avc->lport != 0);
}

static int filter_anyport_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	if (msg->data.avc->port != 0 && filter->anyport == msg->data.avc->port) {
		return 1;
	}
	if (msg->data.avc->source != 0 && filter->anyport == msg->data.avc->source) {
		return 1;
	}
	if (msg->data.avc->dest != 0 && filter->anyport == msg->data.avc->dest) {
		return 1;
	}
	if (msg->data.avc->fport != 0 && filter->anyport == msg->data.avc->fport) {
		return 1;
	}
	if (msg->data.avc->lport != 0 && filter->anyport == msg->data.avc->lport) {
		return 1;
	}
	return 0;
}

static int filter_anyport_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_int_read(&filter->anyport, ch);
}

static void filter_anyport_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_int_print(name, filter->anyport, f, tabs);
}

static bool filter_laddr_is_set(const seaudit_filter_t * filter)
{
	return filter->laddr != NULL;
}

static int filter_laddr_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->laddr != NULL;
}

static int filter_laddr_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return fnmatch(filter->laddr, msg->data.avc->laddr, 0) == 0;
}

static int filter_laddr_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_read(&filter->laddr, ch);
}

static void filter_laddr_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_print(name, filter->laddr, f, tabs);
}

static bool filter_lport_is_set(const seaudit_filter_t * filter)
{
	return filter->lport != 0;
}

static int filter_lport_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->lport != 0;
}

static int filter_lport_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return filter->lport == msg->data.avc->lport;
}

static int filter_lport_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_int_read(&filter->lport, ch);
}

static void filter_lport_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_int_print(name, filter->lport, f, tabs);
}

static bool filter_faddr_is_set(const seaudit_filter_t * filter)
{
	return filter->faddr != NULL;
}

static int filter_faddr_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->faddr != NULL;
}

static int filter_faddr_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return fnmatch(filter->faddr, msg->data.avc->faddr, 0) == 0;
}

static int filter_faddr_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_read(&filter->faddr, ch);
}

static void filter_faddr_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_print(name, filter->faddr, f, tabs);
}

static bool filter_fport_is_set(const seaudit_filter_t * filter)
{
	return filter->fport != 0;
}

static int filter_fport_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->fport != 0;
}

static int filter_fport_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return filter->fport == msg->data.avc->fport;
}

static int filter_fport_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_int_read(&filter->fport, ch);
}

static void filter_fport_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_int_print(name, filter->fport, f, tabs);
}

static bool filter_saddr_is_set(const seaudit_filter_t * filter)
{
	return filter->saddr != NULL;
}

static int filter_saddr_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->saddr != NULL;
}

static int filter_saddr_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return fnmatch(filter->saddr, msg->data.avc->saddr, 0) == 0;
}

static int filter_saddr_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_read(&filter->saddr, ch);
}

static void filter_saddr_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_print(name, filter->saddr, f, tabs);
}

static bool filter_sport_is_set(const seaudit_filter_t * filter)
{
	return filter->sport != 0;
}

static int filter_sport_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->source != 0;
}

static int filter_sport_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return filter->sport == msg->data.avc->source;
}

static int filter_sport_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_int_read(&filter->sport, ch);
}

static void filter_sport_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_int_print(name, filter->sport, f, tabs);
}

static bool filter_daddr_is_set(const seaudit_filter_t * filter)
{
	return filter->daddr != NULL;
}

static int filter_daddr_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->daddr != NULL;
}

static int filter_daddr_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return fnmatch(filter->daddr, msg->data.avc->daddr, 0) == 0;
}

static int filter_daddr_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_read(&filter->daddr, ch);
}

static void filter_daddr_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_print(name, filter->daddr, f, tabs);
}

static bool filter_dport_is_set(const seaudit_filter_t * filter)
{
	return filter->dport != 0;
}

static int filter_dport_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->dest != 0;
}

static int filter_dport_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return filter->dport == msg->data.avc->dest;
}

static int filter_dport_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_int_read(&filter->dport, ch);
}

static void filter_dport_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_int_print(name, filter->dport, f, tabs);
}

static bool filter_port_is_set(const seaudit_filter_t * filter)
{
	return filter->port != 0;
}

static int filter_port_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->port != 0;
}

static int filter_port_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return filter->port == msg->data.avc->port;
}

static int filter_port_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_int_read(&filter->port, ch);
}

static void filter_port_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_int_print(name, filter->port, f, tabs);
}

static bool filter_netif_is_set(const seaudit_filter_t * filter)
{
	return filter->netif != NULL;
}

static int filter_netif_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->netif != NULL;
}

static int filter_netif_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return strcmp(filter->netif, msg->data.avc->netif) == 0;
}

static int filter_netif_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_string_read(&filter->netif, ch);
}

static void filter_netif_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_string_print(name, filter->netif, f, tabs);
}

static bool filter_key_is_set(const seaudit_filter_t * filter)
{
	return filter->key != 0;
}

static int filter_key_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->is_key;
}

static int filter_key_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return filter->key == msg->data.avc->key;
}

static int filter_key_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_int_read(&filter->key, ch);
}

static void filter_key_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_int_print(name, filter->key, f, tabs);
}

static bool filter_cap_is_set(const seaudit_filter_t * filter)
{
	return filter->cap != 0;
}

static int filter_cap_support(const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->is_capability;
}

static int filter_cap_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return filter->key == msg->data.avc->capability;
}

static int filter_cap_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	return filter_int_read(&filter->cap, ch);
}

static void filter_cap_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	filter_int_print(name, filter->cap, f, tabs);
}

static bool filter_avc_msg_type_is_set(const seaudit_filter_t * filter)
{
	return filter->avc_msg_type != SEAUDIT_AVC_UNKNOWN;
}

static int filter_avc_msg_type_support(const seaudit_message_t * msg __attribute__ ((unused)))
{
	return 1;
}

static int filter_avc_msg_type_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && filter->avc_msg_type == msg->data.avc->msg;
}

static int filter_avc_msg_type_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	char *s;
	if ((s = xmlURIUnescapeString((const char *)ch, 0, NULL)) == NULL) {
		return -1;
	}
	filter->avc_msg_type = atoi(s);
	free(s);
	return 0;
}

static void filter_avc_msg_type_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	int i;
	if (filter->avc_msg_type == SEAUDIT_AVC_UNKNOWN) {
		return;
	}
	for (i = 0; i < tabs; i++)
		fprintf(f, "\t");
	fprintf(f, "<criteria type=\"%s\">\n", name);
	for (i = 0; i < tabs + 1; i++) {
		fprintf(f, "\t");
	}
	fprintf(f, "<item>%d</item>\n", filter->avc_msg_type);
	for (i = 0; i < tabs; i++)
		fprintf(f, "\t");
	fprintf(f, "</criteria>\n");
}

static bool filter_date_is_set(const seaudit_filter_t * filter)
{
	return filter->start != NULL;
}

static int filter_date_support(const seaudit_message_t * msg)
{
	return msg->date_stamp != NULL;
}

/**
 * Given two dates compare them.  If both structs have years that are
 * not zeroes then also compare their years.
 */
static int filter_date_comp(const struct tm *t1, const struct tm *t2)
{
	/* tm has year, month, day, hour, min, sec */
	/* check if we should compare the years */
	int retval;
	if (t1->tm_year != 0 && t2->tm_year != 0 && (retval = t1->tm_year - t2->tm_year) != 0) {
		return retval;
	}
	if ((retval = t1->tm_mon - t2->tm_mon) != 0) {
		return retval;
	}
	if ((retval = t1->tm_mday - t2->tm_mday) != 0) {
		return retval;
	}
	if ((retval = t1->tm_hour - t2->tm_hour) != 0) {
		return retval;
	}
	if ((retval = t1->tm_min - t2->tm_min) != 0) {
		return retval;
	}
	if ((retval = t1->tm_sec - t2->tm_sec) != 0) {
		return retval;
	}
	return 0;
}

static int filter_date_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	int compval = filter_date_comp(filter->start, msg->date_stamp);
	if (filter->date_match == SEAUDIT_FILTER_DATE_MATCH_BEFORE) {
		return compval > 0;
	} else if (filter->date_match == SEAUDIT_FILTER_DATE_MATCH_AFTER) {
		return compval < 0;
	} else {
		if (compval > 0)
			return 0;
		compval = filter_date_comp(msg->date_stamp, filter->end);
		return compval < 0;
	}
}

static int filter_date_read(seaudit_filter_t * filter, const xmlChar * ch)
{
	char *s;
	if ((s = xmlURIUnescapeString((const char *)ch, 0, NULL)) == NULL) {
		return -1;
	}
	if (filter->start == NULL) {
		if ((filter->start = calloc(1, sizeof(*(filter->start)))) == NULL) {
			free(s);
			return -1;
		}
		strptime(s, "%a %b %d %T %Y", filter->start);
	} else if (filter->end == NULL) {
		if ((filter->end = calloc(1, sizeof(*(filter->end)))) == NULL) {
			free(s);
			return -1;
		}
		strptime(s, "%a %b %d %T %Y", filter->end);
	} else {
		filter->date_match = atoi(s);
	}
	free(s);
	return 0;
}

static void filter_date_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
{
	int i;
	xmlChar *s, *escaped;
	if (filter->start == NULL) {
		return;
	}
	for (i = 0; i < tabs; i++)
		fprintf(f, "\t");
	fprintf(f, "<criteria type=\"%s\">\n", name);
	s = xmlCharStrdup(asctime(filter->start));
	escaped = xmlURIEscapeStr(s, NULL);
	for (i = 0; i < tabs + 1; i++) {
		fprintf(f, "\t");
	}
	fprintf(f, "<item>%s</item>\n", escaped);
	free(s);
	free(escaped);
	s = xmlCharStrdup(asctime(filter->end));
	escaped = xmlURIEscapeStr(s, NULL);
	for (i = 0; i < tabs + 1; i++)
		fprintf(f, "\t");
	fprintf(f, "<item>%s</item>\n", escaped);
	free(s);
	free(escaped);
	for (i = 0; i < tabs + 1; i++)
		fprintf(f, "\t");
	fprintf(f, "<item>%d</item>\n", filter->date_match);
	for (i = 0; i < tabs; i++)
		fprintf(f, "\t");
	fprintf(f, "</criteria>\n");
}

typedef bool(filter_is_set_func) (const seaudit_filter_t * filter);
typedef int (filter_support_func) (const seaudit_message_t * msg);
typedef int (filter_accept_func) (const seaudit_filter_t * filter, const seaudit_message_t * msg);
typedef void (filter_print_func) (const seaudit_filter_t * filter, const char *name, FILE * f, int tabs);

struct filter_criteria_t
{
	const char *name;
	filter_is_set_func *is_set;
	filter_support_func *support;
	filter_accept_func *accept;
	filter_read_func *read;
	filter_print_func *print;
};

/**
 * Filter criteria are actually implemented as entries within this
 * function pointer table.  During filter_is_accepted() each element
 * of this table is retrieved; if the support functions returns
 * non-zero then the accept function is called.  To add new filter
 * criteria, implement their support and accept functions and then
 * append new entries to this table.
 */
static const struct filter_criteria_t filter_criteria[] = {
	{"src_user", filter_src_user_is_set, filter_src_user_support, filter_src_user_accept, filter_src_user_read,
	 filter_src_user_print},
	{"src_role", filter_src_role_is_set, filter_src_role_support, filter_src_role_accept, filter_src_role_read,
	 filter_src_role_print},
	{"src_type", filter_src_type_is_set, filter_src_type_support, filter_src_type_accept, filter_src_type_read,
	 filter_src_type_print},
	{"src_mls_lvl", filter_src_mls_lvl_is_set, filter_src_mls_lvl_support, filter_src_mls_lvl_accept, filter_src_mls_lvl_read,
	 filter_src_mls_lvl_print},
	{"src_mls_clr", filter_src_mls_clr_is_set, filter_src_mls_clr_support, filter_src_mls_clr_accept, filter_src_mls_clr_read,
	 filter_src_mls_clr_print},
	{"tgt_user", filter_tgt_user_is_set, filter_tgt_user_support, filter_tgt_user_accept, filter_tgt_user_read,
	 filter_tgt_user_print},
	{"tgt_role", filter_tgt_role_is_set, filter_tgt_role_support, filter_tgt_role_accept, filter_tgt_role_read,
	 filter_tgt_role_print},
	{"tgt_type", filter_tgt_type_is_set, filter_tgt_type_support, filter_tgt_type_accept, filter_tgt_type_read,
	 filter_tgt_type_print},
	{"tgt_mls_lvl", filter_tgt_mls_lvl_is_set, filter_tgt_mls_lvl_support, filter_tgt_mls_lvl_accept, filter_tgt_mls_lvl_read,
	 filter_src_mls_lvl_print},
	{"tgt_mls_clr", filter_tgt_mls_clr_is_set, filter_tgt_mls_clr_support, filter_tgt_mls_clr_accept, filter_tgt_mls_clr_read,
	 filter_src_mls_clr_print},
	{"obj_class", filter_tgt_class_is_set, filter_tgt_class_support, filter_tgt_class_accept, filter_tgt_class_read,
	 filter_tgt_class_print},
	{"perm", filter_perm_is_set, filter_perm_support, filter_perm_accept, filter_perm_read, filter_perm_print},
	{"exe", filter_exe_is_set, filter_exe_support, filter_exe_accept, filter_exe_read, filter_exe_print},
	{"host", filter_host_is_set, filter_host_support, filter_host_accept, filter_host_read, filter_host_print},
	{"path", filter_path_is_set, filter_path_support, filter_path_accept, filter_path_read, filter_path_print},
	{"inode", filter_inode_is_set, filter_inode_support, filter_inode_accept, filter_inode_read, filter_inode_print},
	{"pid", filter_pid_is_set, filter_pid_support, filter_pid_accept, filter_pid_read, filter_pid_print},
	{"comm", filter_comm_is_set, filter_comm_support, filter_comm_accept, filter_comm_read, filter_comm_print},
	{"ipaddr", filter_anyaddr_is_set, filter_anyaddr_support, filter_anyaddr_accept, filter_anyaddr_read, filter_anyaddr_print},
	{"anyport", filter_anyport_is_set, filter_anyport_support, filter_anyport_accept, filter_anyport_read,
	 filter_anyport_print},
	{"laddr", filter_laddr_is_set, filter_laddr_support, filter_laddr_accept, filter_laddr_read, filter_laddr_print},
	{"lport", filter_lport_is_set, filter_lport_support, filter_lport_accept, filter_lport_read, filter_lport_print},
	{"faddr", filter_faddr_is_set, filter_faddr_support, filter_faddr_accept, filter_faddr_read, filter_faddr_print},
	{"fport", filter_fport_is_set, filter_fport_support, filter_fport_accept, filter_fport_read, filter_fport_print},
	{"saddr", filter_saddr_is_set, filter_saddr_support, filter_saddr_accept, filter_saddr_read, filter_saddr_print},
	{"sport", filter_sport_is_set, filter_sport_support, filter_sport_accept, filter_sport_read, filter_sport_print},
	{"daddr", filter_daddr_is_set, filter_daddr_support, filter_daddr_accept, filter_daddr_read, filter_daddr_print},
	{"dport", filter_dport_is_set, filter_dport_support, filter_dport_accept, filter_dport_read, filter_dport_print},
	{"port", filter_port_is_set, filter_port_support, filter_port_accept, filter_port_read, filter_port_print},
	{"netif", filter_netif_is_set, filter_netif_support, filter_netif_accept, filter_netif_read, filter_netif_print},
	{"key", filter_key_is_set, filter_key_support, filter_key_accept, filter_key_read, filter_key_print},
	{"cap", filter_cap_is_set, filter_cap_support, filter_cap_accept, filter_cap_read, filter_cap_print},
	{"msg", filter_avc_msg_type_is_set, filter_avc_msg_type_support, filter_avc_msg_type_accept, filter_avc_msg_type_read,
	 filter_avc_msg_type_print},
	{"date_time", filter_date_is_set, filter_date_support, filter_date_accept, filter_date_read, filter_date_print}
};

/******************** protected functions below ********************/

int filter_is_accepted(const seaudit_filter_t * filter, const seaudit_message_t * msg)
{
	bool tried_criterion = false;
	int acceptval;
	size_t i;

	for (i = 0; i < sizeof(filter_criteria) / sizeof(filter_criteria[0]); i++) {
		if (filter_criteria[i].is_set(filter)) {
			tried_criterion = true;
			if (filter_criteria[i].support(msg)) {
				acceptval = filter_criteria[i].accept(filter, msg);
			} else if (filter->strict) {
				/* if filter is strict, then an
				   unsupported criterion is assumed to
				   not match */
				acceptval = 0;
			} else {
				/* for unstrict filters, unsupported
				   criterion is assumed to be a don't
				   care state */
				continue;
			}
			if (filter->match == SEAUDIT_FILTER_MATCH_ANY && acceptval == 1) {
				return 1;
			}
			if (filter->match == SEAUDIT_FILTER_MATCH_ALL && acceptval == 0) {
				return 0;
			}
		}
	}
	if (!tried_criterion) {
		/* if got here, then the filter had no set criterion */
		if (filter->strict) {
			/* a strict empty filter matches nothing */
			return 0;
		}
		return 1;
	}
	if (filter->match == SEAUDIT_FILTER_MATCH_ANY) {
		/* if got here, then no criterion was met */
		return 0;
	}
	/* if got here, then all criteria were met */
	return 1;
}

static bool filter_parse_is_valid_tag(const xmlChar * tag)
{
	static const char *parse_valid_tags[] = { "item", "criteria", "view", "filter", "desc", NULL };
	size_t i;
	for (i = 0; parse_valid_tags[i] != NULL; i++) {
		if (xmlStrcmp(tag, (xmlChar *) parse_valid_tags[i]) == 0) {
			return 1;
		}
	}
	return 0;
}

static filter_read_func *filter_get_read_func(const xmlChar * name)
{
	size_t i;
	for (i = 0; i < sizeof(filter_criteria) / sizeof(filter_criteria[0]); i++) {
		if (xmlStrcmp(name, (xmlChar *) filter_criteria[i].name) == 0) {
			return filter_criteria[i].read;
		}
	}
	return NULL;
}

static void filter_parse_start_element(void *user_data, const xmlChar * name, const xmlChar ** attrs)
{
	struct filter_parse_state *state = user_data;
	size_t i;
	if (!filter_parse_is_valid_tag(name)) {
		state->warnings = 1;
		return;
	}
	if (xmlStrcmp(name, (xmlChar *) "view") == 0) {
		for (i = 0; attrs[i] != NULL && attrs[i + 1] != NULL; i += 2) {
			if (xmlStrcmp(attrs[i], (xmlChar *) "name") == 0) {
				free(state->view_name);
				state->view_name = xmlURIUnescapeString((const char *)attrs[i + 1], 0, NULL);
			} else if (xmlStrcmp(attrs[i], (xmlChar *) "match") == 0) {
				if (xmlStrcmp(attrs[i + 1], (xmlChar *) "all") == 0) {
					state->view_match = SEAUDIT_FILTER_MATCH_ALL;
				} else if (xmlStrcmp(attrs[i + 1], (xmlChar *) "any") == 0) {
					state->view_match = SEAUDIT_FILTER_MATCH_ANY;
				}
			} else if (xmlStrcmp(attrs[i], (xmlChar *) "show") == 0) {
				if (xmlStrcmp(attrs[i + 1], (xmlChar *) "true") == 0) {
					state->view_visible = SEAUDIT_FILTER_VISIBLE_SHOW;
				} else if (xmlStrcmp(attrs[i + 1], (xmlChar *) "hide") == 0) {
					state->view_visible = SEAUDIT_FILTER_VISIBLE_HIDE;
				}
			}
		}
	} else if (xmlStrcmp(name, (xmlChar *) "filter") == 0) {
		/* create a new filter and set it to be the one that is currently being parsed */
		char *filter_name = NULL;
		seaudit_filter_match_e match = SEAUDIT_FILTER_MATCH_ALL;
		bool strict = false;
		for (i = 0; attrs[i] != NULL && attrs[i + 1] != NULL; i += 2) {
			if (xmlStrcmp(attrs[i], (xmlChar *) "name") == 0) {
				free(filter_name);
				filter_name = xmlURIUnescapeString((const char *)attrs[i + 1], 0, NULL);
			} else if (xmlStrcmp(attrs[i], (xmlChar *) "match") == 0) {
				if (xmlStrcmp(attrs[i + 1], (xmlChar *) "all") == 0) {
					match = SEAUDIT_FILTER_MATCH_ALL;
				} else if (xmlStrcmp(attrs[i + 1], (xmlChar *) "any") == 0) {
					match = SEAUDIT_FILTER_MATCH_ANY;
				}
			} else if (xmlStrcmp(attrs[i], (xmlChar *) "strict") == 0) {
				if (xmlStrcmp(attrs[i + 1], (xmlChar *) "true") == 0) {
					strict = true;
				} else if (xmlStrcmp(attrs[i + 1], (xmlChar *) "false") == 0) {
					strict = false;
				}
			}
		}
		if ((state->cur_filter = seaudit_filter_create(filter_name)) != NULL) {
			if (apol_vector_append(state->filters, state->cur_filter) < 0) {
				seaudit_filter_destroy(&state->cur_filter);
			} else {
				seaudit_filter_set_match(state->cur_filter, match);
				seaudit_filter_set_strict(state->cur_filter, strict);
			}
		}
		free(filter_name);
	} else if (xmlStrcmp(name, (xmlChar *) "criteria") == 0) {
		for (i = 0; attrs[i] != NULL && attrs[i + 1] != NULL; i += 2) {
			if (xmlStrcmp(attrs[i], (xmlChar *) "type") == 0) {
				state->cur_filter_read = filter_get_read_func(attrs[i + 1]);
			}
		}
	}
	free(state->cur_string);
	state->cur_string = NULL;
}

static void filter_parse_end_element(void *user_data, const xmlChar * name)
{
	struct filter_parse_state *state = user_data;
	char *s;
	if (!filter_parse_is_valid_tag(name)) {
		state->warnings = 1;
		return;
	}
	if (xmlStrcmp(name, (xmlChar *) "desc") == 0) {
		if (state->cur_filter == NULL) {
			state->warnings = 1;
		} else {
			s = xmlURIUnescapeString((const char *)state->cur_string, 0, NULL);
			seaudit_filter_set_description(state->cur_filter, s);
			free(s);
		}
	} else if (xmlStrcmp(name, (xmlChar *) "item") == 0) {
		if (state->cur_filter == NULL || state->cur_filter_read == NULL) {
			state->warnings = 1;
		} else {
			state->cur_filter_read(state->cur_filter, state->cur_string);
		}
	} else if (xmlStrcmp(name, (xmlChar *) "filter") == 0) {
		state->cur_filter = NULL;
	} else if (xmlStrcmp(name, (xmlChar *) "criteria") == 0) {
		state->cur_filter_read = NULL;
	}
	free(state->cur_string);
	state->cur_string = NULL;
}

static void filter_parse_characters(void *user_data, const xmlChar * ch, int len)
{
	struct filter_parse_state *state = user_data;
	free(state->cur_string);
	state->cur_string = xmlStrndup(ch, len);
}

int filter_parse_xml(struct filter_parse_state *state, const char *filename)
{
	xmlSAXHandler handler;
	int err;

	memset(&handler, 0, sizeof(xmlSAXHandler));
	handler.startElement = filter_parse_start_element;
	handler.endElement = filter_parse_end_element;
	handler.characters = filter_parse_characters;
	err = xmlSAXUserParseFile(&handler, state, filename);
	free(state->cur_string);
	state->cur_string = NULL;
	if (err) {
		errno = EIO;
		return -1;
	}
	if (state->warnings) {
		return 1;
	}
	return 0;
}

void filter_append_to_file(const seaudit_filter_t * filter, FILE * file, int tabs)
{
	xmlChar *escaped;
	xmlChar *str_xml;
	int i;
	size_t j;

	if (filter == NULL || file == NULL) {
		errno = EINVAL;
		return;
	}

	if (filter->name == NULL) {
		str_xml = xmlCharStrdup("Unnamed");
	} else {
		str_xml = xmlCharStrdup(filter->name);
	}
	escaped = xmlURIEscapeStr(str_xml, NULL);
	for (i = 0; i < tabs; i++)
		fprintf(file, "\t");
	fprintf(file, "<filter name=\"%s\" match=\"%s\" strict=\"%s\">\n", escaped,
		filter->match == SEAUDIT_FILTER_MATCH_ALL ? "all" : "any", filter->strict ? "true" : "false");
	free(escaped);
	free(str_xml);

	if (filter->desc != NULL) {
		str_xml = xmlCharStrdup(filter->desc);
		escaped = xmlURIEscapeStr(str_xml, NULL);
		for (i = 0; i < tabs + 1; i++)
			fprintf(file, "\t");
		fprintf(file, "<desc>%s</desc>\n", escaped);
		free(escaped);
		free(str_xml);
	}
	for (j = 0; j < sizeof(filter_criteria) / sizeof(filter_criteria[0]); j++) {
		filter_criteria[j].print(filter, filter_criteria[j].name, file, tabs + 1);
	}
	for (i = 0; i < tabs; i++)
		fprintf(file, "\t");
	fprintf(file, "</filter>\n");
}
