/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Based on mod_skel by
* Anthony Minessale II <anthm@freeswitch.org>
*
* Contributor(s):
*
* Daniel Bryars <danb@aeriandi.com>
* Tim Brown <tim.brown@aeriandi.com>
* Anthony Minessale II <anthm@freeswitch.org>
* William King <william.king@quentustech.com>
* Mike Jerris <mike@jerris.com>
*
* kazoo.c -- Sends FreeSWITCH events to an AMQP broker
*
*/

#include "mod_kazoo.h"

#define KZ_DEFAULT_STREAM_PRE_ALLOCATE 8192

#define KAZOO_DECLARE_GLOBAL_STRING_FUNC(fname, vname) static void __attribute__((__unused__)) fname(const char *string) { if (!string) return;\
		if (vname) {free(vname); vname = NULL;}vname = strdup(string);} static void fname(const char *string)

KAZOO_DECLARE_GLOBAL_STRING_FUNC(set_pref_ip, kazoo_globals.ip);
KAZOO_DECLARE_GLOBAL_STRING_FUNC(set_pref_ei_cookie, kazoo_globals.ei_cookie);
KAZOO_DECLARE_GLOBAL_STRING_FUNC(set_pref_ei_nodename, kazoo_globals.ei_nodename);
//KAZOO_DECLARE_GLOBAL_STRING_FUNC(set_pref_kazoo_var_prefix, kazoo_globals.kazoo_var_prefix);

static int read_cookie_from_file(char *filename) {
	int fd;
	char cookie[MAXATOMLEN + 1];
	char *end;
	struct stat buf;

	if (!stat(filename, &buf)) {
		if ((buf.st_mode & S_IRWXG) || (buf.st_mode & S_IRWXO)) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s must only be accessible by owner only.\n", filename);
			return 2;
		}
		if (buf.st_size > MAXATOMLEN) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s contains a cookie larger than the maximum atom size of %d.\n", filename, MAXATOMLEN);
			return 2;
		}
		fd = open(filename, O_RDONLY);
		if (fd < 1) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to open cookie file %s : %d.\n", filename, errno);
			return 2;
		}

		if (read(fd, cookie, MAXATOMLEN) < 1) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to read cookie file %s : %d.\n", filename, errno);
		}

		cookie[MAXATOMLEN] = '\0';

		/* replace any end of line characters with a null */
		if ((end = strchr(cookie, '\n'))) {
			*end = '\0';
		}

		if ((end = strchr(cookie, '\r'))) {
			*end = '\0';
		}

		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set cookie from file %s: %s\n", filename, cookie);

		set_pref_ei_cookie(cookie);
		return 0;
	} else {
		/* don't error here, because we might be blindly trying to read $HOME/.erlang.cookie, and that can fail silently */
		return 1;
	}
}

void kz_set_hostname()
{
	if (kazoo_globals.hostname == NULL) {
		char hostname[NODENAME_MAX];
		memcpy(hostname, switch_core_get_hostname(), NODENAME_MAX);
		kazoo_globals.hostname_ent = gethostbyname(hostname);
		if(kazoo_globals.hostname_ent != NULL) {
			kazoo_globals.hostname = switch_core_strdup(kazoo_globals.pool, kazoo_globals.hostname_ent->h_name);
		} else {
			kazoo_globals.hostname = switch_core_strdup(kazoo_globals.pool, hostname);
		}
	}
}

switch_status_t kazoo_ei_config(switch_xml_t cfg) {
	switch_xml_t child, param;
	char* kazoo_var_prefix = NULL;
	char* profile_vars_prefix = NULL;
	char* sep_array[KZ_MAX_SEPARATE_STRINGS];
	int array_len, i;
	kazoo_globals.send_all_headers = 0;
	kazoo_globals.send_all_private_headers = 1;
	kazoo_globals.connection_timeout = 500;
	kazoo_globals.ei_receive_timeout = 200;
	kazoo_globals.receive_msg_preallocate = 2000;
	kazoo_globals.event_stream_preallocate = KZ_DEFAULT_STREAM_PRE_ALLOCATE;
	kazoo_globals.send_msg_batch = 10;
	kazoo_globals.event_stream_framing = 2;
	kazoo_globals.event_stream_keepalive = 1;
	kazoo_globals.event_stream_queue_timeout = 200000;
	kazoo_globals.node_receiver_queue_timeout = 100000;
	kazoo_globals.node_sender_queue_timeout = 0;
	kazoo_globals.port = 0;
	kazoo_globals.io_fault_tolerance = 3;
	kazoo_globals.io_fault_tolerance_sleep = 100000; // 100 ms
	kazoo_globals.json_encoding = ERLANG_TUPLE;
	kazoo_globals.delay_before_initial_fetch = 10000000;

	kazoo_globals.legacy_events = SWITCH_FALSE;
	kazoo_globals.expand_headers_on_fetch = SWITCH_TRUE;

	kz_set_tweak(KZ_TWEAK_INTERACTION_ID);
	kz_set_tweak(KZ_TWEAK_EXPORT_VARS);
	kz_set_tweak(KZ_TWEAK_SWITCH_URI);
	kz_set_tweak(KZ_TWEAK_REPLACES_CALL_ID);
	kz_set_tweak(KZ_TWEAK_LOOPBACK_VARS);
	kz_set_tweak(KZ_TWEAK_CALLER_ID);
	kz_set_tweak(KZ_TWEAK_TRANSFERS);
	kz_set_tweak(KZ_TWEAK_BRIDGE);
	kz_set_tweak(KZ_TWEAK_BRIDGE_REPLACES_ALEG);
	kz_set_tweak(KZ_TWEAK_BRIDGE_REPLACES_CALL_ID);
	kz_set_tweak(KZ_TWEAK_BRIDGE_VARIABLES);
	kz_set_tweak(KZ_TWEAK_RESTORE_CALLER_ID_ON_BLIND_XFER);



	if ((child = switch_xml_child(cfg, "settings"))) {
		for (param = switch_xml_child(child, "param"); param; param = param->next) {
			char *var = (char *) switch_xml_attr_soft(param, "name");
			char *val = (char *) switch_xml_attr_soft(param, "value");

			if (!strcmp(var, "listen-ip")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set bind ip address: %s\n", val);
				set_pref_ip(val);
			} else if (!strcmp(var, "listen-port")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set bind port: %s\n", val);
				kazoo_globals.port = atoi(val);
			} else if (!strcmp(var, "cookie")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set cookie: %s\n", val);
				set_pref_ei_cookie(val);
			} else if (!strcmp(var, "cookie-file")) {
				if (read_cookie_from_file(val) == 1) {
					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to read cookie from %s\n", val);
				}
			} else if (!strcmp(var, "nodename")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set node name: %s\n", val);
				set_pref_ei_nodename(val);
			} else if (!strcmp(var, "shortname")) {
				kazoo_globals.ei_shortname = switch_true(val);
			} else if (!strcmp(var, "kazoo-var-prefix")) {
				kazoo_var_prefix = switch_core_strdup(kazoo_globals.pool, val);
			} else if (!strcmp(var, "set-profile-vars-prefix")) {
				profile_vars_prefix = switch_core_strdup(kazoo_globals.pool, val);
			} else if (!strcmp(var, "compat-rel")) {
				if (atoi(val) >= 7)
					kazoo_globals.ei_compat_rel = atoi(val);
				else
					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid compatibility release '%s' specified\n", val);
			} else if (!strcmp(var, "nat-map")) {
				kazoo_globals.nat_map = switch_true(val);
			} else if (!strcmp(var, "send-all-headers")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set send-all-headers: %s\n", val);
				kazoo_globals.send_all_headers = switch_true(val);
			} else if (!strcmp(var, "send-all-private-headers")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set send-all-private-headers: %s\n", val);
				kazoo_globals.send_all_private_headers = switch_true(val);
			} else if (!strcmp(var, "connection-timeout")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set connection-timeout: %s\n", val);
				kazoo_globals.connection_timeout = atoi(val);
			} else if (!strcmp(var, "receive-timeout")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set receive-timeout: %s\n", val);
				kazoo_globals.ei_receive_timeout = atoi(val);
			} else if (!strcmp(var, "receive-msg-preallocate")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set receive-msg-preallocate: %s\n", val);
				kazoo_globals.receive_msg_preallocate = atoi(val);
			} else if (!strcmp(var, "event-stream-preallocate")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set event-stream-preallocate: %s\n", val);
				kazoo_globals.event_stream_preallocate = atoi(val);
			} else if (!strcmp(var, "send-msg-batch-size")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set send-msg-batch-size: %s\n", val);
				kazoo_globals.send_msg_batch = atoi(val);
			} else if (!strcmp(var, "event-stream-framing")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set event-stream-framing: %s\n", val);
				kazoo_globals.event_stream_framing = atoi(val);

			} else if (!strcmp(var, "event-stream-keep-alive")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set event-stream-keep-alive: %s\n", val);
				kazoo_globals.event_stream_keepalive = switch_true(val);

			} else if (!strcmp(var, "io-fault-tolerance")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set io-fault-tolerance: %s\n", val);
				kazoo_globals.io_fault_tolerance = atoi(val);
			} else if (!strcmp(var, "io-fault-tolerance-sleep-micro")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set %s : %s\n", var, val);
				kazoo_globals.io_fault_tolerance_sleep = atoi(val);
			} else if (!strcmp(var, "io-fault-tolerance-sleep-ms")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set %s : %s\n", var, val);
				kazoo_globals.io_fault_tolerance_sleep = atoi(val) * 1000;
			} else if (!strcmp(var, "io-fault-tolerance-sleep-sec")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set %s : %s\n", var, val);
				kazoo_globals.io_fault_tolerance_sleep = atoi(val) * 1000000;


			} else if (!strcmp(var, "node-worker-threads")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set node-worker-threads: %s\n", val);
				kazoo_globals.node_worker_threads = atoi(val);
			} else if (!strcmp(var, "json-term-encoding")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set json-term-encoding: %s\n", val);
				if(!strcmp(val, "map")) {
					kazoo_globals.json_encoding = ERLANG_MAP;
				}
			} else if (!strcmp(var, "legacy-events")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set legacy-events: %s\n", val);
				kazoo_globals.legacy_events = switch_true(val);
			} else if (!strcmp(var, "expand-headers-on-fetch")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set expand-headers-on-fetch: %s\n", val);
				kazoo_globals.expand_headers_on_fetch = switch_true(val);
			} else if (!strcmp(var, "node-receiver-queue-timeout")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set %s : %s\n", var, val);
				kazoo_globals.node_receiver_queue_timeout = atoi(val);
			} else if (!strcmp(var, "node-sender-queue-timeout")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set %s : %s\n", var, val);
				kazoo_globals.node_sender_queue_timeout = atoi(val);
			} else if (!strcmp(var, "event-stream-queue-timeout")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set %s : %s\n", var, val);
				kazoo_globals.event_stream_queue_timeout = atoi(val);
			} else if (!strcmp(var, "delay-before-initial-fetch-micro")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set %s : %s\n", var, val);
				kazoo_globals.delay_before_initial_fetch = atoi(val);
			} else if (!strcmp(var, "delay-before-initial-fetch-ms")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set %s : %s\n", var, val);
				kazoo_globals.delay_before_initial_fetch = atoi(val) * 1000;
			} else if (!strcmp(var, "delay-before-initial-fetch-sec")) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set %s : %s\n", var, val);
				kazoo_globals.delay_before_initial_fetch = atoi(val) * 1000000;
			} else {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "unknown config option %s : %s\n", var, val);
			}
		}
	}

	if ((child = switch_xml_child(cfg, "tweaks"))) {
		char *default_tweaks = (char *) switch_xml_attr_soft(child, "default");
		if (default_tweaks && !zstr(default_tweaks)) {
			int i, v = switch_true(default_tweaks) ? 1 : 0;
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set tweak default : %s\n", default_tweaks);
			for (i = 0; i < KZ_TWEAK_MAX; i++) kazoo_globals.tweaks[i] = v;
		}
		for (param = switch_xml_child(child, "tweak"); param; param = param->next) {
			kz_tweak_t tweak = KZ_TWEAK_MAX;
			char *var = (char *) switch_xml_attr_soft(param, "name");
			char *val = (char *) switch_xml_attr_soft(param, "value");
			if(var && val && kz_name_tweak(var, &tweak) == SWITCH_STATUS_SUCCESS) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set tweak %s : %s\n", var, val);
				if(switch_true(val)) {
					kz_set_tweak(tweak);
				} else {
					kz_clear_tweak(tweak);
				}
			}
		}
	}

	if ((child = switch_xml_child(cfg, "variables"))) {
		for (param = switch_xml_child(child, "variable"); param; param = param->next) {
			char *var = (char *) switch_xml_attr_soft(param, "name");
			char *val = (char *) switch_xml_attr_soft(param, "value");
			if(var && val) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set core variable %s : %s\n", var, val);
				switch_core_set_variable(var, val);
			}
		}
	}

	if ((child = switch_xml_child(cfg, "event-filter"))) {
		switch_hash_t *filter;

		switch_core_hash_init(&filter);
		for (param = switch_xml_child(child, "header"); param; param = param->next) {
			char *var = (char *) switch_xml_attr_soft(param, "name");
			switch_core_hash_insert(filter, var, "1");
		}
		kazoo_globals.event_filter = filter;
	}

	if (kazoo_globals.receive_msg_preallocate < 0) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid receive message preallocate value, disabled\n");
		kazoo_globals.receive_msg_preallocate = 0;
	}

	if (kazoo_globals.event_stream_preallocate < 0) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid event stream preallocate value, disabled\n");
		kazoo_globals.event_stream_preallocate = 0;
	}

	if (kazoo_globals.send_msg_batch < 1) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid send message batch size, reverting to default\n");
		kazoo_globals.send_msg_batch = 10;
	}

	if (kazoo_globals.io_fault_tolerance < 1) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid I/O fault tolerance, reverting to default\n");
		kazoo_globals.io_fault_tolerance = 10;
	}

	if (!kazoo_globals.event_filter) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Event filter not found in configuration, using default\n");
		kazoo_globals.event_filter = create_default_filter();
	}

	if (kazoo_globals.event_stream_framing < 1 || kazoo_globals.event_stream_framing > 4) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid event stream framing value, using default\n");
		kazoo_globals.event_stream_framing = 2;
	}

	if (zstr(kazoo_var_prefix)) {
		kazoo_var_prefix = switch_core_strdup(kazoo_globals.pool, "ecallmgr_;cav_");
	}

	if (zstr(profile_vars_prefix)) {
		profile_vars_prefix = switch_core_strdup(kazoo_globals.pool, "effective_;origination_");
	}

	kazoo_globals.kazoo_var_prefixes = switch_core_alloc(kazoo_globals.pool, sizeof(char*) * KZ_MAX_SEPARATE_STRINGS);
	array_len = switch_separate_string(kazoo_var_prefix, ';', sep_array, KZ_MAX_SEPARATE_STRINGS - 1);
	for(i=0; i < array_len; i++) {
		char var[100];
		sprintf(var, "variable_%s", sep_array[i]);
		kazoo_globals.kazoo_var_prefixes[i] = switch_core_strdup(kazoo_globals.pool, var);
	}

	kazoo_globals.profile_vars_prefixes = switch_core_alloc(kazoo_globals.pool, sizeof(char*) * KZ_MAX_SEPARATE_STRINGS);
	array_len = switch_separate_string(profile_vars_prefix, ';', sep_array, KZ_MAX_SEPARATE_STRINGS - 1);
	for(i=0; i < array_len; i++) {
		kazoo_globals.profile_vars_prefixes[i] = switch_core_strdup(kazoo_globals.pool, sep_array[i]);
	}

	if (!kazoo_globals.node_worker_threads) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Number of node worker threads not found in configuration, using default\n");
		kazoo_globals.node_worker_threads = 10;
	}

	if (zstr(kazoo_globals.ip)) {
		set_pref_ip("0.0.0.0");
	}

	if (zstr(kazoo_globals.ei_cookie)) {
		int res;
		char *home_dir = getenv("HOME");
		char path_buf[1024];

		if (!zstr(home_dir)) {
			/* $HOME/.erlang.cookie */
			switch_snprintf(path_buf, sizeof (path_buf), "%s%s%s", home_dir, SWITCH_PATH_SEPARATOR, ".erlang.cookie");
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Checking for cookie at path: %s\n", path_buf);

			res = read_cookie_from_file(path_buf);
			if (res) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No cookie or valid cookie file specified, using default cookie\n");
				set_pref_ei_cookie("ClueCon");
			}
		}
	}

	if (!kazoo_globals.ei_nodename) {
		set_pref_ei_nodename("freeswitch");
	}

	if (!kazoo_globals.nat_map) {
		kazoo_globals.nat_map = 0;
	}

	return SWITCH_STATUS_SUCCESS;
}

switch_status_t kazoo_config_handlers(switch_xml_t cfg)
{
		switch_xml_t def = NULL;
		switch_xml_t child, param;
		char* xml = NULL;
		kazoo_config_ptr definitions = NULL, fetch_handlers = NULL, event_handlers = NULL;
		kazoo_event_profile_ptr events = NULL;

		xml = strndup(kz_default_config, kz_default_config_size);
		def = switch_xml_parse_str_dup(xml);

		kz_xml_process(def);
		kz_xml_process(cfg);

		if ((child = switch_xml_child(cfg, "variables"))) {
			for (param = switch_xml_child(child, "variable"); param; param = param->next) {
				char *var = (char *) switch_xml_attr_soft(param, "name");
				char *val = (char *) switch_xml_attr_soft(param, "value");
				if(var && val) {
					switch_core_set_variable(var, val);
				}
			}
		} else if ((child = switch_xml_child(def, "variables"))) {
			for (param = switch_xml_child(child, "variable"); param; param = param->next) {
				char *var = (char *) switch_xml_attr_soft(param, "name");
				char *val = (char *) switch_xml_attr_soft(param, "value");
				if(var && val) {
					switch_core_set_variable(var, val);
				}
			}
		}

		definitions = kazoo_config_definitions(cfg);
		if(definitions == NULL) {
			if(kazoo_globals.definitions == NULL) {
				definitions = kazoo_config_definitions(def);
			} else {
				definitions = kazoo_globals.definitions;
			}
		}

		fetch_handlers = kazoo_config_fetch_handlers(definitions, cfg);
		if(fetch_handlers == NULL) {
			if(kazoo_globals.fetch_handlers == NULL) {
				fetch_handlers = kazoo_config_fetch_handlers(definitions, def);
			} else {
				fetch_handlers = kazoo_globals.fetch_handlers;
			}
		}

		event_handlers = kazoo_config_event_handlers(definitions, cfg);
		if(event_handlers == NULL) {
			if(kazoo_globals.event_handlers == NULL) {
				event_handlers = kazoo_config_event_handlers(definitions, def);
			} else {
				event_handlers = kazoo_globals.event_handlers;
			}
		}

		if(event_handlers != NULL) {
			events = (kazoo_event_profile_ptr) switch_core_hash_find(event_handlers->hash, "default");
		}

		if(events == NULL) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to get default handler for events\n");
			destroy_config(&event_handlers);
			event_handlers = kazoo_config_event_handlers(definitions, def);
			events = (kazoo_event_profile_ptr) switch_core_hash_find(event_handlers->hash, "default");
		}

		if(kazoo_globals.events != events) {
			bind_event_profiles(events->events);
			kazoo_globals.events = events;
		}

		if(kazoo_globals.event_handlers != event_handlers) {
			kazoo_config_ptr tmp = kazoo_globals.event_handlers;
			kazoo_globals.event_handlers = event_handlers;
			destroy_config(&tmp);
		}

		if(kazoo_globals.fetch_handlers != fetch_handlers) {
			kazoo_config_ptr tmp = kazoo_globals.fetch_handlers;
			kazoo_globals.fetch_handlers = fetch_handlers;
			rebind_fetch_profiles(fetch_handlers);
			destroy_config(&tmp);
		}

		if(kazoo_globals.definitions != definitions) {
			kazoo_config_ptr tmp = kazoo_globals.definitions;
			kazoo_globals.definitions = definitions;
			destroy_config(&tmp);
		}


		switch_xml_free(def);
		switch_safe_free(xml);

	return SWITCH_STATUS_SUCCESS;
}

switch_status_t kazoo_load_config()
{
	char *cf = "kazoo.conf";
	switch_xml_t cfg, xml;
	if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open configuration file %s\n", cf);
		return SWITCH_STATUS_FALSE;
	} else {
		kazoo_ei_config(cfg);
		kazoo_config_handlers(cfg);
		switch_xml_free(xml);
	}

	return SWITCH_STATUS_SUCCESS;
}

void kazoo_destroy_config()
{
	destroy_config(&kazoo_globals.event_handlers);
	destroy_config(&kazoo_globals.fetch_handlers);
	destroy_config(&kazoo_globals.definitions);
}

switch_status_t kazoo_config_events(kazoo_config_ptr definitions, switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_event_profile_ptr profile)
{
	switch_xml_t events, event;
	kazoo_event_ptr prv = NULL, cur = NULL;


	if ((events = switch_xml_child(cfg, "events")) != NULL) {
		for (event = switch_xml_child(events, "event"); event; event = event->next) {
			const char *var = switch_xml_attr(event, "name");
			cur = (kazoo_event_ptr) switch_core_alloc(pool, sizeof(kazoo_event_t));
			memset(cur, 0, sizeof(kazoo_event_t));
			if(prv == NULL) {
				profile->events = prv = cur;
			} else {
				prv->next = cur;
				prv = cur;
			}
			cur->profile = profile;
			cur->name = switch_core_strdup(pool, var);
			kazoo_config_filters(pool, event, &cur->filter);
			kazoo_config_fields(definitions, pool, event, &cur->fields);
			if (switch_xml_child(event, "logging") != NULL) {
				kazoo_config_loglevels(pool, event, &cur->logging);
			}
		}
	}

	return SWITCH_STATUS_SUCCESS;

}


switch_status_t kazoo_config_fetch_handler(kazoo_config_ptr definitions, kazoo_config_ptr root, switch_xml_t cfg, kazoo_fetch_profile_ptr *ptr)
{
	kazoo_fetch_profile_ptr profile = NULL;
	switch_xml_t params, param;
	switch_xml_section_t fetch_section;
	int fetch_timeout = 2000000;
	switch_memory_pool_t *pool = NULL;

	char *name = (char *) switch_xml_attr_soft(cfg, "name");

	if (zstr(name)) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "missing name in profile\n");

		return SWITCH_STATUS_GENERR;
	}

	if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error allocation pool for new profile : %s\n", name);

		return SWITCH_STATUS_GENERR;
	}

	profile = switch_core_alloc(pool, sizeof(kazoo_fetch_profile_t));
	profile->pool = pool;
	profile->root = root;
	profile->name = switch_core_strdup(profile->pool, name);

	fetch_section = switch_xml_parse_section_string(name);

	if ((params = switch_xml_child(cfg, "params")) != NULL) {

		for (param = switch_xml_child(params, "param"); param; param = param->next) {
			char *var = (char *) switch_xml_attr_soft(param, "name");
			char *val = (char *) switch_xml_attr_soft(param, "value");

			if (!var) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Profile[%s] param missing 'name' attribute\n", name);
				continue;
			}

			if (!val) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Profile[%s] param[%s] missing 'value' attribute\n", name, var);
				continue;
			}

			if (!strncmp(var, "fetch-timeout", 13)) {
				fetch_timeout = atoi(val);
			} else if (!strncmp(var, "fetch-section", 13)) {
				fetch_section = switch_xml_parse_section_string(val);
			}
		}
	}

	if (fetch_section == SWITCH_XML_SECTION_RESULT) {
		char *tmp = switch_xml_toxml(cfg, SWITCH_FALSE);
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Fetch Profile[%s] invalid fetch-section: %s\n", name, tmp);
		free(tmp);
		goto err;
	}


	profile->fetch_timeout = fetch_timeout;
	profile->section = fetch_section;
	kazoo_config_fields(definitions, pool, cfg, &profile->fields);
	kazoo_config_loglevels(pool, cfg, &profile->logging);

	if(root) {
		if ( switch_core_hash_insert(root->hash, name, (void *) profile) != SWITCH_STATUS_SUCCESS) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "failed to insert new fetch profile [%s] into kazoo profile hash\n", name);
			goto err;
		}
	}

	if (ptr) {
		*ptr = profile;
	}

	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "fetch handler profile %s successfully configured\n", name);

	return SWITCH_STATUS_SUCCESS;

 err:
	/* Cleanup */
	if(pool) {
		switch_core_destroy_memory_pool(&pool);
	}

	return SWITCH_STATUS_GENERR;

}

switch_status_t kazoo_config_event_handler(kazoo_config_ptr definitions, kazoo_config_ptr root, switch_xml_t cfg, kazoo_event_profile_ptr *ptr)
{
	kazoo_event_profile_ptr profile = NULL;
	switch_memory_pool_t *pool = NULL;

	char *name = (char *) switch_xml_attr_soft(cfg, "name");
	if (zstr(name)) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "missing name in profile\n");
		return SWITCH_STATUS_GENERR;
	}

	if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error allocation pool for new profile : %s\n", name);
		return SWITCH_STATUS_GENERR;
	}

	profile = switch_core_alloc(pool, sizeof(kazoo_event_profile_t));
	profile->pool = pool;
	profile->root = root;
	profile->name = switch_core_strdup(profile->pool, name);

	kazoo_config_filters(pool, cfg, &profile->filter);
	kazoo_config_fields(definitions, pool, cfg, &profile->fields);
	kazoo_config_events(definitions, pool, cfg, profile);
	kazoo_config_loglevels(pool, cfg, &profile->logging);

	if(root) {
		if ( switch_core_hash_insert(root->hash, name, (void *) profile) != SWITCH_STATUS_SUCCESS) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to insert new profile [%s] into kazoo profile hash\n", name);
			goto err;
		}
	}

	if(ptr)
		*ptr = profile;

	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "event handler profile %s successfully configured\n", name);
	return SWITCH_STATUS_SUCCESS;

 err:
	/* Cleanup */
    if(pool) {
    	switch_core_destroy_memory_pool(&pool);
    }
	return SWITCH_STATUS_GENERR;

}



/* For Emacs:
 * Local Variables:
 * mode:c
 * indent-tabs-mode:t
 * tab-width:4
 * c-basic-offset:4
 * End:
 * For VIM:
 * vim:set softtabstop=4 shiftwidth=4 tabstop=4
 */
