/*
** SellaNMS
** $Id: gui.cxx,v 1.26 2006/01/24 07:08:10 sella Exp $
** Copyright (c) 2001-2005 Digital Genesis Software, LLC. All Rights Reserved.
** Released under the GPL Version 2 License.
** http://www.digitalgenesis.com
*/

static const char rcsid[] = "$Id: gui.cxx,v 1.26 2006/01/24 07:08:10 sella Exp $";

#include "gui.h"
#include "daemon.h"
#include "storage/storagedata.h"
#include "storage/storage.h"
#include <stdio.h>
#include <unistd.h>
#include <sched.h>
#include <sys/stat.h>
#include <stl/slist>
#include <stl/string>
#include <stl/vector>

//#define LIBDBI065_HACK

extern ConfigNode* config;
extern bool interrupt;

Gui::Gui() {
}

Gui::~Gui() {
}

RESULT Gui::execute() {
	RESULT result = SUCCESS;
	variable_binding_t polInput;
	variable_binding_t polOutput;

	time_t next;

   int err;
   pthread_t thread_id = 0;

   TRACE(TRACE_THREAD, "Gui::execute() starting");

   /* Create the expire thread */
   if ((err = pthread_create(&thread_id, NULL, (void* (*)(void*))Gui::expire_thread, this)) == 0) {
      TRACE(TRACE_INTERNAL, "expire thread created.");
   } else {
      LMSG("failed to create expire thread.");
		interrupt = true;
		result = FAILURE;
	}

	while (!interrupt) {
		next = time(NULL) + this->interval / OUTPUT_GUI_UPDATEINTERVAL_MLT;

		if (readOutput("gui") == FAILURE) {
			interrupt = true;
			result = FAILURE;
			continue;
		}

		if (output.size() <= 0) {
			/* Wait until next run. */
			while (time(NULL) < next && !interrupt) {
				usleep(USLEEP_LONGLOOP_TIME);
			}
			continue;
		}

		while (!interrupt && policyInputNext(polInput) == SUCCESS) {
			/* Clear the output container. */
			polOutput.clear();
			polInput["process"] = "gui";
			polInput["origin"] = "storage";
			polInput["event"] = "update";

			/* Run data though the input policy. */
			switch (inputPolicy.evaluate(polOutput, polInput)) {
				case ACTION_FAILURE:
					LMSG("failed to apply input policy.");

					interrupt = true;
					result = FAILURE;
					continue;

				case ACTION_REJECT:
					continue;
			}

			/* Make the switch to data for the output policy. */
			if (!inputPolicy.empty()) {
				polInput = polOutput;
				polOutput.clear();
			}

			/* Run data though the output policy. */
			switch (outputPolicy.evaluate(polOutput, polInput)) {
				case ACTION_FAILURE:
					LMSG("failed to apply output policy.");

					interrupt = true;
					result = FAILURE;
					continue;

				case ACTION_REJECT:
					continue;
			}

			/* Process accepted data. */
			processPolicyOutput(polOutput);
		}

		/* Short sleep. */
#ifdef _POSIX_PRIORITY_SCHEDULING
		sched_yield();
#else
		usleep(0);
#endif
	}

	TRACE(TRACE_INTERNAL, "joining expire thread %lu", thread_id);
	if ((err = pthread_join(thread_id, NULL)) != 0) {
		LMSG("pthread_join returned %d", err);
		result = FAILURE;
	}

	TRACE(TRACE_THREAD, "Gui::execute() exiting");

	return result;
}

RESULT Gui::readConfig() {
	RESULT result = SUCCESS;

	vector<string> v;

	ConfigNode node;

	if (!config->assignNode(getConfigPath(), node)) {
		LMSG("No '%s' section found within the config file.", getConfigPath());

		return FAILURE;
	}

	node.assignValue("interval", interval, OUTPUT_GUI_UPDATEINTERVAL_DEF, OUTPUT_GUI_UPDATEINTERVAL_MIN, OUTPUT_GUI_UPDATEINTERVAL_MAX, OUTPUT_GUI_UPDATEINTERVAL_MLT);
	node.assignValue("expire-interval", expireInterval, OUTPUT_GUI_EXPIREINTERVAL_DEF, OUTPUT_GUI_EXPIREINTERVAL_MIN, OUTPUT_GUI_EXPIREINTERVAL_MAX, OUTPUT_GUI_EXPIREINTERVAL_MLT);
	node.assignValue("queue-depth", queueDepth, OUTPUT_GUI_QUEUEDEPTH_DEF, OUTPUT_GUI_QUEUEDEPTH_MIN, OUTPUT_GUI_QUEUEDEPTH_MAX);
	node.assignValue("max-delete", maxDelete, OUTPUT_GUI_MAXDELETE_DEF, OUTPUT_GUI_MAXDELETE_MIN, OUTPUT_GUI_MAXDELETE_MAX);

	/* Clear any existing policies. */
	inputPolicy.clear();
	outputPolicy.clear();

	/* Add and compile input policy. */
	if (node.assignValues("policy input", v)) {
		LMSG("input policies are not implemented yet");
		if (inputPolicy.addPolicies(config, v) == FAILURE) {
			LMSG("failed to add/compile input policies");
			result = FAILURE;
		}
	}

	/* Add and compile output policy. */
	if (node.assignValues("policy output", v)) {
		if (outputPolicy.addPolicies(config, v) == FAILURE) {
			LMSG("failed to add/compile output policies");
			result = FAILURE;
		}
	}

	/* Verify we have required policies. */
	if (outputPolicy.empty()) {
		LMSG("a output policy is required for this module");
		result = FAILURE;
	}

	/* Configure Storage */
	if (storage.setConfig(*config) != SUCCESS) {
		result = FAILURE;
	}

	/* Read used statees from config. */
   if (getPolicyStateList(stateList) != SUCCESS) {
      LMSG("failed to locate any state within output policy");

      result = FAILURE;
   }

	printConfig();

	return result;
}

void Gui::printConfig() {
	string str;
	vector<string> v;
	vector<string>::iterator v_it;

	if (!TRACE_ISSET(TRACE_CONFIG)) {
		return;
	}

	TRACE(TRACE_CONFIG, "interval: %lu sec", interval / OUTPUT_GUI_UPDATEINTERVAL_MLT);
	TRACE(TRACE_CONFIG, "expire-interval: %lu sec", expireInterval / OUTPUT_GUI_EXPIREINTERVAL_MLT);
	TRACE(TRACE_CONFIG, "queue-depth: %lu", queueDepth);
	TRACE(TRACE_CONFIG, "max-delete: %lu", maxDelete);

	str = "state: [ ";
	for (slist<string>::iterator it = stateList.begin(); it != stateList.end(); ++it) {
		str.append(*it + " ");
	}
	str.append("]");
	TRACE(TRACE_CONFIG, str.c_str());

	if (config->assignValues("output gui policy input", v)) {
		str.erase();
		for (v_it = v.begin(); v_it != v.end(); ++v_it) {
			str.append(*v_it + " ");
		}
		TRACE(TRACE_CONFIG, "policy input: %s", str.c_str());
	} else {
		TRACE(TRACE_CONFIG, "policy input: none");
	}

	if (config->assignValues("output gui policy output", v)) {
		str.erase();
		for (v_it = v.begin(); v_it != v.end(); ++v_it) {
			str.append(*v_it + " ");
		}
		TRACE(TRACE_CONFIG, "policy output: %s", str.c_str());
	} else {
		TRACE(TRACE_CONFIG, "policy output: none");
	}
}

RESULT Gui::readOutput(const string &module) {
	RESULT result = SUCCESS;

	output_t out;
	string str;
	StorageData data;
	slist<unsigned long> output_ids;

	if (storage.queryf(data, "SELECT id, UNIX_TIMESTAMP(timestamp) AS unix_timestamp, element, interface, ip, source, module, protocol, channel, state, brief, detail FROM output WHERE module = '%s' ORDER BY timestamp LIMIT %lu", module.c_str(), queueDepth) == SUCCESS) {
		while (!interrupt && data.nextRow()) {
			data.getULong(out.id, "id");

			//No idea what libdbi thinks the type should be. Its angry about every type.
			data.getAuto(str, "unix_timestamp");
			out.unix_timestamp = (time_t) atol(str.c_str());

			data.getLong(out.element, "element");

			data.getLong(out.interface, "interface");

			data.getLong(out.ip, "ip");

			data.getString(out.source, "source");

			data.getString(out.module, "module");

			data.getString(out.protocol, "protocol");

			data.getString(out.channel, "channel");

			data.getString(out.state, "state");

			data.getString(out.brief, "brief");

			data.getBinary(out.detail, "detail");

			output.push_front(out);

			output_ids.push_front(out.id);
		}
		result = SUCCESS;
	} else {
		LMSG("failed to read output data from storage");
		result = FAILURE;
	}

	if (!output_ids.empty()) {
		result = deleteOutput(output_ids);
	}

	return result;
}

RESULT Gui::policyInputNext(variable_binding_t &policy_input) {
	char buf[256];
	output_t out;

	tm ts;

	if (output.empty()) {
		return FAILURE;
	}

	policy_input.clear();

	out = output.front();
	output.pop_front();

	/* Element */
	policy_input["element-id"] = out.element;

	/* Interface */
	policy_input["interface-id"] = out.interface;

	/* IPAddress */
	policy_input["ip-id"] = out.ip;

	/* Source */
	policy_input["source"] = out.source;

	/* Module */
	policy_input["module"] = out.module;

	/* Protocol */
	policy_input["protocol"] = out.protocol;

	/* Channel */
	policy_input["channel"] = out.channel;

	/* State */
	policy_input["state"] = out.state;

	/* Brief */
	policy_input["brief"] = out.brief;

	/* Detail */
	policy_input["detail"] = out.detail;

	/* Setup the time. */
	localtime_r(&out.unix_timestamp, &ts);

	/* Date/Time */
	strftime(buf, sizeof(buf), "%m/%d/%Y %H:%M:%S", &ts);
	policy_input["date-time"] = buf;

	/* Date */
	strftime(buf, sizeof(buf), "%m/%d/%Y", &ts);
	policy_input["date"] = buf;

	/* Day of Week */
	strftime(buf, sizeof(buf), "%a", &ts);
	policy_input["day-of-week"] = buf;

	/* Begin and End Time */
	strftime(buf, sizeof(buf), "%H:%M", &ts);
	policy_input["begin-time"] = buf;
	policy_input["end-time"] = buf;

	/* Queue Size */
	snprintf(buf, sizeof(buf), "%lu", output.size() + 1);
	policy_input["queue-size"] = buf;

	return SUCCESS;
}

RESULT Gui::deleteExpiredEntries(slist<string> &expired_list) {
	RESULT result = SUCCESS;

	string sql;
	StorageData data;

	TRACE(TRACE_DEBUG, "%lu expired entries pending delete", expired_list.size());

	if (expired_list.size() < 1) {
		return result;
	}

	sql = "DELETE FROM gui_event WHERE ";
	for (slist<string>::iterator it = expired_list.begin(); it != expired_list.end(); ++it) {
		sql.append("id = " + *it + " OR ");
	}
	/* Remove last ' OR '. */
	sql.erase(sql.size() - 4);

	if ((result = storage.query(data, sql.c_str())) != SUCCESS) {
		LMSG("failed to delete from gui_event");
	}

	expired_list.clear();

	return result;
}

RESULT Gui::buildExpirePolicyInput(variable_binding_t &policy_input, StorageData &data) {
	RESULT result = SUCCESS;

	data.getULong(policy_input["id"], "id");
	data.getString(policy_input["age"], "age");
	data.getULong(policy_input["element-id"], "element");
	data.getULong(policy_input["interface-id"], "interface");
	data.getULong(policy_input["ip-id"], "ip");
	data.getString(policy_input["source"], "source");
	data.getString(policy_input["protocol"], "protocol");
	data.getString(policy_input["channel"], "channel");
	data.getString(policy_input["state"], "state");
	data.getString(policy_input["brief"], "brief");
#ifdef LIBDBI065_HACK
	policy_input["detail"] = "gui.cxx::buildExpirePolicyInput() - #define LIBDBI065_HACK";
#else
	data.getBinary(policy_input["detail"], "detail");
#endif

	return result;
}

RESULT Gui::expire_thread(void* args) {
	RESULT result = SUCCESS;
	ACTION action;

	time_t next;

	StorageData data;
	variable_binding_t policy_input;
	variable_binding_t policy_output;
	slist<string> expired_list;
	const char sql[] = "SELECT id, CONCAT(UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timestamp)) AS age, element, interface, ip, source, protocol, channel, state, brief, detail FROM gui_event";

	Gui *instance = (Gui*) args;

	TRACE(TRACE_THREAD, "Gui::expire_thread() starting main loop");


	while (!interrupt) {
		next = time(NULL) + instance->expireInterval / OUTPUT_GUI_EXPIREINTERVAL_MLT;

		if ((result = instance->storage.query(data, sql)) != SUCCESS) {
			LMSG("failed to select from gui_event");

			interrupt = true;

			break;
		} else {
			TRACE(TRACE_DEBUG, "%lu entries to run though expire policies", data.getNumRow());
		}

		while (data.nextRow() && !interrupt) {
			if ((result = instance->buildExpirePolicyInput(policy_input, data)) != SUCCESS) {
				LMSG("failed to build policy input");

				interrupt = true;
				break;
			}

			policy_input["process"] = "gui";
			policy_input["origin"] = "storage";
			policy_input["event"] = "expire";

			/* Run policy on input reader. */
			if ((action = instance->outputPolicy.evaluate(policy_output, policy_input)) == ACTION_FAILURE) {
				LMSG("failed to evaluate policy within expire thread");

				result = FAILURE;
				interrupt = true;
				break;
			} else if (action == ACTION_REJECT) {
				expired_list.push_front(policy_output["id"]);
			}

			/* Clear structures. */
			policy_input.clear();
			policy_output.clear();

			/* Slow down loop. No need to consume CPU. */
			usleep(USLEEP_FASTLOOP_TIME);

			/* Partial delete. */
			if (expired_list.size() >= instance->maxDelete) {
				instance->deleteExpiredEntries(expired_list);
			}
		}

		/* Clear data */
		data.clear();

		instance->deleteExpiredEntries(expired_list);

		TRACE(TRACE_DEBUG, "expire iteration complete");
		TRACE(TRACE_TIMER, "expire iteration completed in %d secs", (instance->expireInterval / OUTPUT_GUI_EXPIREINTERVAL_MLT) - (next - time(NULL)));

		/* Wait until next run. */
		while (!interrupt && time(NULL) < next) {
			usleep(USLEEP_LONGLOOP_TIME);
		}
	}

	TRACE(TRACE_THREAD, "Gui::expire_thread() exiting");

	pthread_exit(NULL);
}

RESULT Gui::processPolicyOutput(variable_binding_t &policy_output) {
	char buf[512], *tok, *p;
	string str, action;
	slist<string> labels, fields, states;
	StorageData data;

	/* Read in entries in match-label-list */
	if (policy_output.find("match-label-list") != policy_output.end()) {
		snprintf(buf, sizeof(buf), "%s", policy_output["match-label-list"].c_str());
		TRACE(TRACE_INTERNAL, "match-label-list: %s", buf);

		for (tok = strtok_r(buf, ",;: \t", &p); tok != NULL; tok = strtok_r(NULL, ",;: \t", &p)) {
			TRACE(TRACE_DUMP, "label token: %s", tok);
			labels.push_front(tok);
		}
		labels.reverse();
	}
	
	/* Process all lables. */
	for (slist<string>::iterator lit = labels.begin(); lit != labels.end(); ++lit) {
		/* Read in policy settings. */

		/* Collect fields to match. */
		fields.clear();
		snprintf(buf, sizeof(buf), "%s-match-field", lit->c_str());
		if (policy_output.find(buf) != policy_output.end()) {
			str = policy_output[buf];
			TRACE(TRACE_INTERNAL, "%s: %s", buf, str.c_str());

			if (str.find("element") != string::npos) {
				fields.push_front("element");
			}

			if (str.find("interface") != string::npos) {
				fields.push_front("interface");
			}

			if (str.find("ip") != string::npos) {
				fields.push_front("ip");
			}

			if (str.find("source") != string::npos) {
				fields.push_front("source");
			}

			if (str.find("protocol") != string::npos) {
				fields.push_front("protocol");
			}

			if (str.find("channel") != string::npos) {
				fields.push_front("channel");
			}

			if (str.find("state") != string::npos) {
				fields.push_front("state");
			}

			if (str.find("brief") != string::npos) {
				fields.push_front("brief");
			}

			if (str.find("detail") != string::npos) {
				fields.push_front("detail");
			}
		} else {
			TRACE(TRACE_INTERNAL, "%s: (not set) - skipping", buf);

			continue;
		}

		/* Collect states to match against. */
		states.clear();
		snprintf(buf, sizeof(buf), "%s-match-state", lit->c_str());
		if (policy_output.find(buf) != policy_output.end()) {
			str = policy_output[buf];
			TRACE(TRACE_INTERNAL, "%s: %s", buf, str.c_str());

			for (slist<string>::iterator it = stateList.begin(); it != stateList.end(); ++it) {
				if (str.find(*it) != string::npos) {
					states.push_front(*it);
				}
			}
		}

		/* Collect action */
		snprintf(buf, sizeof(buf), "%s-match-action", lit->c_str());
		if (policy_output.find(buf) != policy_output.end()) {
			action = policy_output[buf];
			TRACE(TRACE_INTERNAL, "%s: %s", buf, action.c_str());
		} else {
			TRACE(TRACE_INTERNAL, "%s: (not set) - skipping", buf);

			continue;
		}

		/* Process Actions */

		/* Clear entries based on policy, if requested. */
		if (action.find("clear") != string::npos && !fields.empty()) {
			str = "DELETE FROM gui_event WHERE ";

			/* Insert fields into statement */
			for (slist<string>::iterator it = fields.begin(); it != fields.end(); ++it) {
				if (it != fields.begin()) {
					str.append(" AND ");
				}

				TRACE(TRACE_DUMP, "checking field: %s", it->c_str());

				if (it->compare("element") == 0) {
					if (policy_output["element-id"].empty()) {
						str.append("element IS NULL");
					} else {
						str.append("element = " + policy_output["element-id"]);
					}
				} else if (it->compare("interface") == 0) {
					if (policy_output["interface-id"].empty()) {
						str.append("interface IS NULL");
					} else {
						str.append("interface = " + policy_output["interface-id"]);
					}
				} else if (it->compare("ip") == 0) {
					if (policy_output["ip-id"].empty()) {
						str.append("ip IS NULL");
					} else {
						str.append("ip = " + policy_output["ip-id"]);
					}
				} else if (it->compare("source") == 0) {
					if (policy_output["source"].empty()) {
						str.append("source IS NULL");
					} else {
						str.append("source = " + data.quoteString(policy_output["source"]));
					}
				} else if (it->compare("protocol") == 0) {
					str.append(" protocol = " + data.quoteString(policy_output["protocol"]));
				} else if (it->compare("channel") == 0) {
					if (policy_output["channel"].empty()) {
						str.append("channel IS NULL");
					} else {
						str.append("channel = " + data.quoteString(policy_output["channel"]));
					}
				} else if (it->compare("state") == 0 && states.empty()) {
					if (policy_output["state"].empty()) {
						str.append("state IS NULL");
					} else {
						str.append("state = " + data.quoteString(policy_output["state"]));
					}
				} else if (it->compare("brief") == 0) {
					if (policy_output["brief"].empty()) {
						str.append("brief IS NULL");
					} else {
						str.append("brief = " + data.quoteString(policy_output["brief"]));
					}
				} else if (it->compare("detail") == 0) {
					if (policy_output["detail"].empty()) {
						str.append("detail IS NULL");
					} else {
						str.append("detail = " + data.quoteString(policy_output["detail"]));
					}
				}
			}

			/* Insert states into statement */
			for (slist<string>::iterator it = states.begin(); it != states.end(); ++it) {
				if (it == states.begin()) {
					str.append(" AND (");
				} else if (it != states.begin() && it != states.end()) {
					str.append(" OR ");
				}

				str.append("state = '" + *it + "'");
			}
			if (!states.empty()) {
				str.append(")");
			}

			if (TRACE_ISSET(TRACE_DEBUG)) {
				TRACE(TRACE_DEBUG, "clearing entry for channel %s - elemenet: %s interface: %s ip: %s", policy_output["channel"].empty() ? "null" : policy_output["channel"].c_str(), policy_output["element-id"].empty() ? "null" : policy_output["element-id"].c_str(), policy_output["interface-id"].empty() ? "null" : policy_output["interface-id"].c_str(), policy_output["ip-id"].empty() ? "null" : policy_output["ip-id"].c_str());
			}

			/* Execute statement. */
			if (storage.query(data, str.c_str()) != SUCCESS) {
				TRACE(TRACE_DEBUG, "failed to delete from storage in Gui::processPolicyOutput(). SQL: %s", str.c_str());

				return FAILURE;
			}
		} else {
			TRACE(TRACE_INTERNAL, "%s-match-action: %s (unknown) - skipping", lit->c_str(), buf);

			continue;
		}
 
	}

	/* Insert new alarm */
	str = "INSERT INTO gui_event SET ";

	if (policy_output.find("timestamp") != policy_output.end()) {
		str.append("gui_event.timestamp = " + policy_output["timestamp"] + ", ");
	}

	if (policy_output.find("element-id") != policy_output.end()) {
		if (!policy_output["element-id"].empty()) {
			str.append("gui_event.element = " + policy_output["element-id"] + ", ");
		}
	}

	if (policy_output.find("interface-id") != policy_output.end()) {
		if (!policy_output["interface-id"].empty()) {
			str.append("gui_event.interface = " + policy_output["interface-id"] + ", ");
		}
	}

	if (policy_output.find("ip-id") != policy_output.end()) {
		if (!policy_output["ip-id"].empty()) {
			str.append("gui_event.ip = " + policy_output["ip-id"] + ", ");
		}
	}

	if (policy_output.find("source") != policy_output.end()) {
		str.append("gui_event.source = " + data.quoteString(policy_output["source"]) + ", ");
	}

	if (policy_output.find("protocol") != policy_output.end()) {
		str.append("gui_event.protocol = " + data.quoteString(policy_output["protocol"]) + ", ");
	}

	if (policy_output.find("channel") != policy_output.end()) {
		str.append("gui_event.channel = " + data.quoteString(policy_output["channel"]) + ", ");
	}

	if (policy_output.find("state") != policy_output.end()) {
		str.append("gui_event.state = " + data.quoteString(policy_output["state"]) + ", ");
	}

	if (policy_output.find("brief") != policy_output.end()) {
		str.append("gui_event.brief = " + data.quoteString(policy_output["brief"]) + ", ");
	}

	if (policy_output.find("detail") != policy_output.end()) {
		str.append("gui_event.detail = " + data.quoteString(policy_output["detail"]) + ", ");
	}
	str.erase(str.size() - 2);

	if (TRACE_ISSET(TRACE_DEBUG)) {
		TRACE(TRACE_DEBUG, "inserting entry for channel %s - elemenet: %s interface: %s ip: %s", policy_output["channel"].empty() ? "null" : policy_output["channel"].c_str(), policy_output["element-id"].empty() ? "null" : policy_output["element-id"].c_str(), policy_output["interface-id"].empty() ? "null" : policy_output["interface-id"].c_str(), policy_output["ip-id"].empty() ? "null" : policy_output["ip-id"].c_str());
	}

	/* Execute INSERT statement. */
	if (storage.query(data, str.c_str()) != SUCCESS) {
		LMSG("failed to insert into storage in Gui::processPolicyOutput().");

		return FAILURE;
	}

	return SUCCESS;
}

RESULT Gui::deleteOutput(slist<unsigned long> &output_ids) {
	RESULT result = SUCCESS;

	string sql;
	char buf[132];
	StorageData data;

	if (!output_ids.empty()) {
		/* Build DELETE string. */
		sql = "DELETE FROM output WHERE ";
		for (slist<unsigned long>::iterator it = output_ids.begin(); it != output_ids.end(); ++it) {
			snprintf(buf, 132, "id = %lu OR ", *it);
			sql.append(buf);
		}
		/* Remove the last ' OR ' */
		sql.erase(sql.size() - 4);

		if (storage.query(data, sql.c_str()) != SUCCESS) {
			LMSG("failed to delete output data from storage.");
			result = FAILURE;
		}
	}

	return result;
}

RESULT Gui::getPolicyStateList(slist<string> &stateList) {
	RESULT result = SUCCESS;

	slist<unsigned int>::iterator it;
	variable_reference_t referenced;

	if (this->outputPolicy.getReferencedVariables(referenced) != SUCCESS) {
		LMSG("unable to get referenced variables from output policies.");

		result = FAILURE;
	} else {
		/* Walk policy terms. */
		for (variable_reference_t::iterator term = referenced.begin(); term != referenced.end(); ++term) {
			for (slist<variable_t>::iterator variable = term->begin(); variable != term->end(); ++variable) {
				if (variable->name.compare("state") == 0) {
					for (slist<string>::iterator event = variable->values.begin(); event != variable->values.end(); ++event) {
						stateList.push_front(*event);
					}
				}
			}
		}

		/* Return a sorted unique list. */
		stateList.sort();
		stateList.unique();
	}

	if (TRACE_ISSET(TRACE_DEBUG) && stateList.empty()) {
		LMSG("no state lists found within applied policies");
	}

	return result;
}

/*
** Local Variables:
** c-basic-offset: 3
** tab-width: 3
** End:
** vim:
** vim: noet ts=3 sw=3
*/
