/*-
 * Copyright (c) 2000-2003 Andrey Simonenko
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "config.h"

#ifndef lint
static const char rcsid[] ATTR_UNUSED =
  "@(#)$Id: ipa_db.c,v 1.1.4.2 2007/05/11 16:29:59 simon Exp $";
#endif /* !lint */

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/queue.h>

#include "ipa_mod.h"

#include "dlapi.h"
#include "confcommon.h"
#include "memfunc.h"
#include "parser.h"

#include "ipa_ac.h"
#include "ipa_db.h"
#include "ipa_cmd.h"
#include "ipa_ctl.h"
#include "ipa_time.h"

#include "ipa_conf.h"
#include "ipa_log.h"
#include "ipa_main.h"
#include "ipa_rules.h"
#include "ipa_autorules.h"

u_int		ndb_mods;		/* Number of db_mods. */

int		debug_db_null;		/* debug_db_null parameter. */

const struct db_list *global_db_list;	/* global { db_list } */

/*
 * db_sets is the list of all used "db_list" parameters.
 * If some rules use the same "db_list" parameter, then
 * they share the same struct db_list{}.
 */
struct db_sets db_sets = SLIST_HEAD_INITIALIZER(db_sets);

/*
 * List of all database modules.
 */
struct db_mod_list db_mod_list;

/*
 * Find a database module by configuration prefix.
 */
struct db_mod *
db_mod_by_prefix(const char *prefix)
{
	struct db_mod *db_mod;

	SLIST_FOREACH(db_mod, &db_mod_list, link)
		if (strcmp(db_mod->ipa_db_mod->conf_prefix, prefix) == 0)
			break;
	return db_mod;
}

/*
 * Find a database module by a name.
 */
struct db_mod *
db_mod_by_name(const char *name)
{
	struct db_mod *db_mod;

	SLIST_FOREACH(db_mod, &db_mod_list, link)
		if (strcmp(db_mod->ipa_db_mod->db_name, name) == 0)
			return db_mod;

	return NULL;
}

/*
 * Release memory held by db_set, including struct db_set{}.
 */
void
free_db_set(struct db_set *set)
{
	struct db_elem *db, *db_next;

	for (db = STAILQ_FIRST(&set->list); db != NULL; db = db_next) {
		db_next = STAILQ_NEXT(db, link);
		mem_free(db, m_anon);
	}
	mem_free(set, m_anon);
}

/*
 * Release memory held by all db_set.
 */
void
free_db_lists(void)
{
	struct db_set *set;

	while ( (set = SLIST_FIRST(&db_sets)) != NULL) {
		SLIST_REMOVE_HEAD(&db_sets, link);
		free_db_set(set);
	}
}

#ifdef WITH_LIMITS

# ifdef WITH_RULES
/*
 * Init limits for one static rule in databases.
 */
static int
db_init_statlimits(const struct rule *rule)
{
	struct limit *limit;
	const struct db_elem *db;

	STAILQ_FOREACH(limit, &rule->limits, link) {
		if (!STAILQ_EMPTY(limit->db_list)) {
			STAILQ_FOREACH(db, limit->db_list, link)
				if (db->ipa_db_mod->db_init_statlimit != NULL) {
					if (db->ipa_db_mod->db_init_statlimit(
					    rule->ruleno, rule->rule_name, rule->rule_info,
					    limit->limitno, limit->limit_name, limit->limit_info)
					    < 0) {
						logmsgx(IPA_LOG_ERR, "module %s: db_init_statlimit(%s, %s) failed",
						    db->mod_file, rule->rule_name, limit->limit_name);
						return -1;
					}
				} else {
					logmsgx(IPA_LOG_ERR, "module %s: db_init_statlimit(%s, %s): module does not support limits from static rules",
					    db->mod_file, rule->rule_name, limit->limit_name);
					return -1;
				}
		} else {
			if (debug_db_null)
				logmsgx(IPA_LOG_INFO, "rule %s, limit %s: this limit uses builtin \"null\" database",
				    rule->rule_name, limit->limit_name);
		}
		mem_free(limit->limit_info, m_parser);
		limit->limit_info = NULL;
	}
	return 0;
}
# endif /* WITH_RULES */


# ifdef WITH_AUTORULES
/*
 * Init limits for one dynamic rule in databases.
 */
static int
db_init_dynlimits(const struct autorule *autorule, const struct rule *rule)
{
	struct limit *limit;
	const struct db_elem *db;

	STAILQ_FOREACH(limit, &rule->limits, link) {
		if (!STAILQ_EMPTY(limit->db_list)) {
			STAILQ_FOREACH(db, limit->db_list, link)
				if (db->ipa_db_mod->db_init_dynlimit != NULL) {
					if (db->ipa_db_mod->db_init_dynlimit(
					    autorule->aruleno,
					    rule->ruleno, rule->rule_name, rule->rule_info,
					    limit->limitno, limit->limit_name, limit->limit_info)
					    < 0) {
						logmsgx(IPA_LOG_ERR, "module %s: db_init_dynlimit(%s, %s, %s) failed",
						    db->mod_file, autorule->arule_name, rule->rule_name, limit->limit_name);
						return -1;
					}
				} else {
					logmsgx(IPA_LOG_ERR, "module %s: db_init_dynlimit(%s, %s, %s): module does not support limits from dynamic rules",
					    db->mod_file, autorule->arule_name, rule->rule_name, limit->limit_name);
					return -1;
				}
		} else {
			if (debug_db_null)
				logmsgx(IPA_LOG_INFO, "rule %s, limit %s: this limit uses builtin \"null\" database",
				    rule->rule_name, limit->limit_name);
		}
	}
	return 0;
}

# endif /* WITH_AUTORULES */

/*
 * Deinit limit in one rule in databases.
 */
int
db_deinit_limit(const struct rule *rule, const struct limit *limit)
{
	int	error = 0;
	const struct db_elem *db;

	STAILQ_FOREACH(db, limit->db_list, link)
		if (db->ipa_db_mod->db_deinit_limit != NULL)
			if (db->ipa_db_mod->db_deinit_limit(rule->ruleno, limit->limitno) < 0) {
				logmsgx(IPA_LOG_ERR, "module %s: db_deinit_limit(%s, %s) failed",
				    db->mod_file, rule->rule_name, limit->limit_name);
					error = -1;
			}
	return error;
}

/*
 * Update statistics for one limit.
 */
int
db_update_limit(const struct rule *rule, const struct limit *limit)
{
	int	error = 0;
	const struct db_elem *db;

	STAILQ_FOREACH(db, limit->db_list, link)
		if (db->ipa_db_mod->db_update_limit(rule->ruleno, limit->limitno,
		    &limit->cnt, &curdate) < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: db_update_limit(%s, %s) failed",
			    db->mod_file, rule->rule_name, limit->limit_name);
			error = -1;
		}

	return error;
}

/*
 * Register limit event for one limit.
 */
int
db_limit_event(const struct rule *rule, struct limit *limit,
    u_int event, const ipa_tm *tm)
{
	int	error = 0;
	const struct db_elem *db;

	if (ctl_enable) {
		limit->event_date_set |= 1 << event;
		limit->event_date[event] = *tm;
	}

	STAILQ_FOREACH(db, limit->db_list, link)
		if (db->ipa_db_mod->db_limit_event(rule->ruleno, limit->limitno,
		    event, tm, &curdate) < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: db_limit_event(%s, %s, %s) failed",
			    db->mod_file, rule->rule_name, limit->limit_name, limit_event_msg[event]);
			error = -1;
		}
	return error;
}

/*
 * Get a state of the limit: use first database which
 * supports db_get_limit_state method and save its name in
 * db_name.  If any of databases used by the limit doesn't
 * support db_get_limit_state method, then set db_name to NULL.
 */
int
db_get_limit_state(const struct rule *rule, const struct limit *limit,
    struct ipa_limit_state *state, const char **db_name)
{
	const struct db_elem *db;

	STAILQ_FOREACH(db, limit->db_list, link)
		if (db->ipa_db_mod->db_get_limit_state != NULL) {
			if (db->ipa_db_mod->db_get_limit_state(rule->ruleno, limit->limitno, state) < 0) {
				logmsgx(IPA_LOG_ERR, "module %s: db_get_limit_state(%s, %s) failed",
				    db->mod_file, rule->rule_name, limit->limit_name);
				return -1;
			}
			*db_name = db->ipa_db_mod->db_name;
			return 0;
		}
	*db_name = NULL;
	return 0;
}

/*
 * Set a state of the limit in databases.
 */
int
db_set_limit_state(const struct rule *rule, struct limit *limit,
    struct ipa_limit_state *state, int new_state)
{
	int	error = 0;
	const struct db_elem *db;

	state->event_date_set |= IPA_LIMIT_EVENT_UPDATED_SET;
	state->event_date[IPA_LIMIT_EVENT_UPDATED] = curdate;

	if (ctl_enable) {
		limit->event_date_set = state->event_date_set;
		memcpy(&limit->event_date, &state->event_date, sizeof limit->event_date);
	}

	STAILQ_FOREACH(db, limit->db_list, link)
		if (db->ipa_db_mod->db_set_limit_state(rule->ruleno, limit->limitno, state, new_state) < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: db_set_limit_state(%s, %s, %d) failed",
			    db->mod_file, rule->rule_name, limit->limit_name, new_state);
			error = -1;
		}
	return error;
}

/*
 * Set a limit active/inactive in databases it uses.
 */
int
db_set_limit_active(const struct rule *rule, const struct limit *limit,
    int active)
{
	const struct db_elem *db;

	/* Limit uses own db_list. */
	STAILQ_FOREACH(db, limit->db_list, link)
		if (db->ipa_db_mod->db_set_limit_active != NULL)
			if (db->ipa_db_mod->db_set_limit_active(rule->ruleno,
			    limit->limitno, active) < 0) {
				logmsgx(IPA_LOG_ERR, "module %s: db_set_limit_active(%s, %s, %d) failed",
				    db->mod_file, rule->rule_name, limit->limit_name, active);
				return -1;
			}

	return 0;
}
#endif /* WITH_LIMITS */

#ifdef WITH_THRESHOLDS

# ifdef WITH_RULES
/*
 * Init thresholds for one static rule in databases.
 */
static int
db_init_statthresholds(const struct rule *rule)
{
	struct threshold *threshold;
	const struct db_elem *db;

	STAILQ_FOREACH(threshold, &rule->thresholds, link) {
		if (!STAILQ_EMPTY(threshold->db_list)) {
			STAILQ_FOREACH(db, threshold->db_list, link)
				if (db->ipa_db_mod->db_init_statthreshold != NULL) {
					if (db->ipa_db_mod->db_init_statthreshold(
					    rule->ruleno, rule->rule_name, rule->rule_info,
					    threshold->thresholdno, threshold->threshold_name, threshold->threshold_info)
					    < 0) {
						logmsgx(IPA_LOG_ERR, "module %s: db_init_statthreshold(%s, %s) failed",
						    db->mod_file, rule->rule_name, threshold->threshold_name);
						return -1;
					}
				} else {
					logmsgx(IPA_LOG_ERR, "module %s: db_init_statthreshold(%s, %s): module does not support thresholds from static rules",
					    db->mod_file, rule->rule_name, threshold->threshold_name);
					return -1;
				}
		} else {
			if (debug_db_null)
				logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: this threshold uses builtin \"null\" database",
				    rule->rule_name, threshold->threshold_name);
		}
		mem_free(threshold->threshold_info, m_parser);
		threshold->threshold_info = NULL;
	}
	return 0;
}
# endif /* WITH_RULES */

# ifdef WITH_AUTORULES
/*
 * Init thresholds for one dynamic rule in databases.
 */
static int
db_init_dynthresholds(const struct autorule *autorule, const struct rule *rule)
{
	struct threshold *threshold;
	const struct db_elem *db;

	STAILQ_FOREACH(threshold, &rule->thresholds, link) {
		if (!STAILQ_EMPTY(threshold->db_list)) {
			STAILQ_FOREACH(db, threshold->db_list, link)
				if (db->ipa_db_mod->db_init_dynthreshold != NULL) {
					if (db->ipa_db_mod->db_init_dynthreshold(
					    autorule->aruleno,
					    rule->ruleno, rule->rule_name, rule->rule_info,
					    threshold->thresholdno, threshold->threshold_name, threshold->threshold_info)
					    < 0) {
						logmsgx(IPA_LOG_ERR, "module %s: db_init_dynthreshold(%s, %s, %s) failed",
						    db->mod_file, autorule->arule_name, rule->rule_name, threshold->threshold_name);
						return -1;
					}
				} else {
					logmsgx(IPA_LOG_ERR, "module %s: db_init_dynthreshold(%s, %s, %s): module does not support thresholds from dynamic rules",
					    db->mod_file, autorule->arule_name, rule->rule_name, threshold->threshold_name);
					return -1;
				}
		} else {
			if (debug_db_null)
				logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: this threshold uses builtin \"null\" database",
				    rule->rule_name, threshold->threshold_name);
		}
	}
	return 0;
}
# endif /* WITH_AUTORULES */

/*
 * Deinit threshold in one rule in databases.
 */
int
db_deinit_threshold(const struct rule *rule, const struct threshold *threshold)
{
	int	error = 0;
	const struct db_elem *db;

	STAILQ_FOREACH(db, threshold->db_list, link)
		if (db->ipa_db_mod->db_deinit_threshold != NULL)
			if (db->ipa_db_mod->db_deinit_threshold(rule->ruleno, threshold->thresholdno) < 0) {
				logmsgx(IPA_LOG_ERR, "module %s: db_deinit_threshold(%s, %s) failed",
				    db->mod_file, rule->rule_name, threshold->threshold_name);
				error = -1;
			}
	return error;
}

/*
 * Update statistics for one threshold in databases.
 */
int
db_update_threshold(const struct rule *rule, const struct threshold *threshold)
{
	int	error = 0;
	const struct db_elem *db;

	STAILQ_FOREACH(db, threshold->db_list, link)
		if (db->ipa_db_mod->db_update_threshold(rule->ruleno, threshold->thresholdno,
		    &threshold->cnt, &threshold->tm_from, &threshold->tm_updated) < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: db_update_threshold(%s, %s) failed",
			    db->mod_file, rule->rule_name, threshold->threshold_name);
			error = -1;
		}
	return error;
}

/*
 * Get a state of the threshold: use first database which
 * supports db_get_threshold_state method and save its name in
 * db_name.  If any of databases used by the threshold doesn't
 * support db_get_threshold_state method, then set db_name to NULL.
 */
int
db_get_threshold_state(const struct rule *rule, const struct threshold *threshold,
    struct ipa_threshold_state *state, const char **db_name)
{
	const struct db_elem *db;

	STAILQ_FOREACH(db, threshold->db_list, link)
		if (db->ipa_db_mod->db_get_threshold_state != NULL) {
			if (db->ipa_db_mod->db_get_threshold_state(rule->ruleno, threshold->thresholdno, state) < 0) {
				logmsgx(IPA_LOG_ERR, "module %s: db_get_threshold_state(%s, %s) failed",
				    db->mod_file, rule->rule_name, threshold->threshold_name);
				return -1;
			}
			*db_name = db->ipa_db_mod->db_name;
			return 0;
		}
	*db_name = NULL;
	return 0;
}

/*
 * Set a state of the threshold in databases.
 */
int
db_set_threshold_state(const struct rule *rule, const struct threshold *threshold,
    const struct ipa_threshold_state *state)
{
	int	error = 0;
	const struct db_elem *db;

	STAILQ_FOREACH(db, threshold->db_list, link)
		if (db->ipa_db_mod->db_set_threshold_state(rule->ruleno, threshold->thresholdno, state) < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: db_set_threshold_state(%s, %s) failed",
			    db->mod_file, rule->rule_name, threshold->threshold_name);
			error = -1;
		}
	return error;
}

/*
 * Set a threshold active/inactive in databases it uses.
 */
int
db_set_threshold_active(const struct rule *rule,
    const struct threshold *threshold, int active)
{
	const struct db_elem *db;

	/* Threshold uses own db_list. */
	STAILQ_FOREACH(db, threshold->db_list, link)
		if (db->ipa_db_mod->db_set_threshold_active != NULL)
			if (db->ipa_db_mod->db_set_threshold_active(rule->ruleno,
			    threshold->thresholdno, active) < 0) {
				logmsgx(IPA_LOG_ERR, "module %s: db_set_threshold_active(%s, %s, %d) failed",
				    db->mod_file, rule->rule_name, threshold->threshold_name, active);
				return -1;
			}

	return 0;
}
#endif /* WITH_THRESHOLDS */

#ifdef WITH_RULES
/*
 * Init one static rule in databases, also init its limits and thresholds.
 */
int
db_init_statrule(struct rule *rule)
{
	const struct db_elem *db;

	if (!STAILQ_EMPTY(rule->db_list)) {
		STAILQ_FOREACH(db, rule->db_list, link)
			if (db->ipa_db_mod->db_init_statrule != NULL) {
				if (db->ipa_db_mod->db_init_statrule(rule->ruleno,
				    rule->rule_name, rule->rule_info) < 0) {
					logmsgx(IPA_LOG_ERR, "module %s: db_init_statrule(%s) failed",
					    db->mod_file, rule->rule_name);
					return -1;
				}
			} else {
				logmsgx(IPA_LOG_ERR, "module %s: db_init_statrule(%s): module does not support static rules",
				    db->mod_file, rule->rule_name);
				return -1;
			}
	} else {
		if (debug_db_null)
			logmsgx(IPA_LOG_INFO, "rule %s: this rule uses builtin \"null\" database",
			    rule->rule_name);
	}

	mem_free(rule->rule_info, m_parser);
	rule->rule_info = NULL;

#ifdef WITH_LIMITS
	if (db_init_statlimits(rule) < 0)
		return -1;
#endif
#ifdef WITH_THRESHOLDS
	if (db_init_statthresholds(rule) < 0)
		return -1;
#endif

	return 0;
}
#endif /* WITH_RULES */

/*
 * Pre-init all db_mods.
 */
int
pre_init_db_mods(void)
{
	const struct db_mod *db_mod;

	SLIST_FOREACH(db_mod, &db_mod_list, link)
		if (db_mod->ipa_db_mod->db_pre_init() < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: db_pre_init failed",
			    db_mod->mod_file);
			return -1;
		}

	return 0;
}

/*
 * Init all db_mods.
 */
int
init_db_mods(void)
{
	const struct db_mod *db_mod;

	SLIST_FOREACH(db_mod, &db_mod_list, link)
		if (db_mod->ipa_db_mod->db_init() < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: db_init failed",
			    db_mod->mod_file);
			return -1;
		}

	return 0;
}

/*
 * Deinit all db_mods.
 */
int
deinit_db_mods(void)
{
	int	error = 0;
	const struct db_mod *db_mod;

	SLIST_FOREACH(db_mod, &db_mod_list, link)
		if (db_mod->ipa_db_mod->db_deinit() < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: db_deinit failed", db_mod->mod_file);
			error = -1;
		}

	return error;
}

/*
 * Deinit one rule in databases.
 */
int
db_deinit_rule(const struct rule *rule)
{
	int	error = 0;
	const struct db_elem *db;

	STAILQ_FOREACH(db, rule->db_list, link) {
		if (db->ipa_db_mod->db_deinit_rule(rule->ruleno) < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: db_deinit_rule(%s) failed",
			    db->mod_file, rule->rule_name);
			error = -1;
		}
	}

	return error;
}

/*
 * Update statistics for one rule.
 */
int
db_update_rule(const struct rule *rule, const uint64_t *cnt)
{
	int	error = 0;
	const struct db_elem *db;

	STAILQ_FOREACH(db, rule->db_list, link)
		if (db->ipa_db_mod->db_update_rule(rule->ruleno, cnt, &curdate) < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: db_update_rule(%s) failed",
			    db->mod_file, rule->rule_name);
			error = -1;
		}
	return error;
}

/*
 * Append an accounting record for one rule.
 */
int
db_append_rule(struct rule *rule, const uint64_t *cnt, int flush_cnt)
{
	int	error = 0;
	const struct db_elem *db;

	STAILQ_FOREACH(db, rule->db_list, link)
		if (db->ipa_db_mod->db_append_rule(rule->ruleno, cnt, &curdate) < 0) {
			logmsgx(IPA_LOG_ERR, "module %s: db_append_rule(%s) failed",
			    db->mod_file, rule->rule_name);
			error = -1;
		}

	if (rule->append_tevent != NULL)
		rule->append_sec = rule->append_tevent->event_sec;

	if (flush_cnt)
		rule->cnt = UINT64_C(0);

	return error;
}

/*
 * Set a rule active/inactive in databases it uses.
 */
int
db_set_rule_active(const struct rule *rule, int active)
{
	const struct db_elem *db;

	STAILQ_FOREACH(db, rule->db_list, link)
		if (db->ipa_db_mod->db_set_rule_active != NULL)
			if (db->ipa_db_mod->db_set_rule_active(rule->ruleno, active) < 0) {
				logmsgx(IPA_LOG_ERR, "module %s: db_set_rule_active(%s, %d) failed",
				    db->mod_file, rule->rule_name, active);
				return -1;
			}

	return 0;
}

#ifdef WITH_AUTORULES
/*
 * Init one dynamic rule in databases, also init its limits and thresholds.
 */
int
db_init_dynrule(const struct autorule *autorule, struct rule *rule)
{
	const struct db_elem *db;

	if (!STAILQ_EMPTY(rule->db_list)) {
		STAILQ_FOREACH(db, rule->db_list, link)
			if (db->ipa_db_mod->db_init_dynrule != NULL) {
				if (db->ipa_db_mod->db_init_dynrule(autorule->aruleno, rule->ruleno,
				    rule->rule_name, rule->rule_info) < 0) {
					logmsgx(IPA_LOG_ERR, "module %s: db_init_dynrule(%s, %s) failed",
					    db->mod_file, autorule->arule_name, rule->rule_name);
					return -1;
				}
			} else {
				logmsgx(IPA_LOG_ERR, "module %s: db_init_dynrule(%s, %s): module does not support dynamic rules",
				    db->mod_file, autorule->arule_name, rule->rule_name);
				return -1;
			}
	} else {
		if (debug_db_null)
			logmsgx(IPA_LOG_INFO, "rule %s: this rule uses builtin \"null\" database",
			    rule->rule_name);
	}

	mem_free(rule->rule_info, m_anon);
	rule->rule_info = NULL;

#ifdef WITH_LIMITS
	if (db_init_dynlimits(autorule, rule) < 0)
		return -1;
#endif
#ifdef WITH_THRESHOLDS
	if (db_init_dynthresholds(autorule, rule) < 0)
		return -1;
#endif

	return 0;
}
#endif /* WITH_AUTORULES */


syntax highlighted by Code2HTML, v. 0.9.1