/*
 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 * Copyright (C) 2005-2020, 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.
 *
 * Contributor(s):
 *
 * Anthony Minessale II <anthm@freeswitch.org>
 * Michael Jerris <mike@jerris.com>
 *
 *
 * switch_channel.c -- Media Channel Interface
 *
 */

#include <switch.h>
#include <switch_channel.h>
#include <pcre.h>

struct switch_cause_table {
	const char *name;
	switch_call_cause_t cause;
};

typedef struct switch_device_state_binding_s {
	switch_device_state_function_t function;
	void *user_data;
	struct switch_device_state_binding_s *next;
} switch_device_state_binding_t;

static struct {
	switch_memory_pool_t *pool;
	switch_hash_t *device_hash;
	switch_mutex_t *device_mutex;
	switch_device_state_binding_t *device_bindings;
} globals;

static struct switch_cause_table CAUSE_CHART[] = {
	{"NONE", SWITCH_CAUSE_NONE},
	{"UNALLOCATED_NUMBER", SWITCH_CAUSE_UNALLOCATED_NUMBER},
	{"NO_ROUTE_TRANSIT_NET", SWITCH_CAUSE_NO_ROUTE_TRANSIT_NET},
	{"NO_ROUTE_DESTINATION", SWITCH_CAUSE_NO_ROUTE_DESTINATION},
	{"CHANNEL_UNACCEPTABLE", SWITCH_CAUSE_CHANNEL_UNACCEPTABLE},
	{"CALL_AWARDED_DELIVERED", SWITCH_CAUSE_CALL_AWARDED_DELIVERED},
	{"NORMAL_CLEARING", SWITCH_CAUSE_NORMAL_CLEARING},
	{"USER_BUSY", SWITCH_CAUSE_USER_BUSY},
	{"NO_USER_RESPONSE", SWITCH_CAUSE_NO_USER_RESPONSE},
	{"NO_ANSWER", SWITCH_CAUSE_NO_ANSWER},
	{"SUBSCRIBER_ABSENT", SWITCH_CAUSE_SUBSCRIBER_ABSENT},
	{"CALL_REJECTED", SWITCH_CAUSE_CALL_REJECTED},
	{"NUMBER_CHANGED", SWITCH_CAUSE_NUMBER_CHANGED},
	{"REDIRECTION_TO_NEW_DESTINATION", SWITCH_CAUSE_REDIRECTION_TO_NEW_DESTINATION},
	{"EXCHANGE_ROUTING_ERROR", SWITCH_CAUSE_EXCHANGE_ROUTING_ERROR},
	{"DESTINATION_OUT_OF_ORDER", SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER},
	{"INVALID_NUMBER_FORMAT", SWITCH_CAUSE_INVALID_NUMBER_FORMAT},
	{"FACILITY_REJECTED", SWITCH_CAUSE_FACILITY_REJECTED},
	{"RESPONSE_TO_STATUS_ENQUIRY", SWITCH_CAUSE_RESPONSE_TO_STATUS_ENQUIRY},
	{"NORMAL_UNSPECIFIED", SWITCH_CAUSE_NORMAL_UNSPECIFIED},
	{"NORMAL_CIRCUIT_CONGESTION", SWITCH_CAUSE_NORMAL_CIRCUIT_CONGESTION},
	{"NETWORK_OUT_OF_ORDER", SWITCH_CAUSE_NETWORK_OUT_OF_ORDER},
	{"NORMAL_TEMPORARY_FAILURE", SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE},
	{"SWITCH_CONGESTION", SWITCH_CAUSE_SWITCH_CONGESTION},
	{"ACCESS_INFO_DISCARDED", SWITCH_CAUSE_ACCESS_INFO_DISCARDED},
	{"REQUESTED_CHAN_UNAVAIL", SWITCH_CAUSE_REQUESTED_CHAN_UNAVAIL},
	{"PRE_EMPTED", SWITCH_CAUSE_PRE_EMPTED},
	{"FACILITY_NOT_SUBSCRIBED", SWITCH_CAUSE_FACILITY_NOT_SUBSCRIBED},
	{"OUTGOING_CALL_BARRED", SWITCH_CAUSE_OUTGOING_CALL_BARRED},
	{"INCOMING_CALL_BARRED", SWITCH_CAUSE_INCOMING_CALL_BARRED},
	{"BEARERCAPABILITY_NOTAUTH", SWITCH_CAUSE_BEARERCAPABILITY_NOTAUTH},
	{"BEARERCAPABILITY_NOTAVAIL", SWITCH_CAUSE_BEARERCAPABILITY_NOTAVAIL},
	{"SERVICE_UNAVAILABLE", SWITCH_CAUSE_SERVICE_UNAVAILABLE},
	{"BEARERCAPABILITY_NOTIMPL", SWITCH_CAUSE_BEARERCAPABILITY_NOTIMPL},
	{"CHAN_NOT_IMPLEMENTED", SWITCH_CAUSE_CHAN_NOT_IMPLEMENTED},
	{"FACILITY_NOT_IMPLEMENTED", SWITCH_CAUSE_FACILITY_NOT_IMPLEMENTED},
	{"SERVICE_NOT_IMPLEMENTED", SWITCH_CAUSE_SERVICE_NOT_IMPLEMENTED},
	{"INVALID_CALL_REFERENCE", SWITCH_CAUSE_INVALID_CALL_REFERENCE},
	{"INCOMPATIBLE_DESTINATION", SWITCH_CAUSE_INCOMPATIBLE_DESTINATION},
	{"INVALID_MSG_UNSPECIFIED", SWITCH_CAUSE_INVALID_MSG_UNSPECIFIED},
	{"MANDATORY_IE_MISSING", SWITCH_CAUSE_MANDATORY_IE_MISSING},
	{"MESSAGE_TYPE_NONEXIST", SWITCH_CAUSE_MESSAGE_TYPE_NONEXIST},
	{"WRONG_MESSAGE", SWITCH_CAUSE_WRONG_MESSAGE},
	{"IE_NONEXIST", SWITCH_CAUSE_IE_NONEXIST},
	{"INVALID_IE_CONTENTS", SWITCH_CAUSE_INVALID_IE_CONTENTS},
	{"WRONG_CALL_STATE", SWITCH_CAUSE_WRONG_CALL_STATE},
	{"RECOVERY_ON_TIMER_EXPIRE", SWITCH_CAUSE_RECOVERY_ON_TIMER_EXPIRE},
	{"MANDATORY_IE_LENGTH_ERROR", SWITCH_CAUSE_MANDATORY_IE_LENGTH_ERROR},
	{"PROTOCOL_ERROR", SWITCH_CAUSE_PROTOCOL_ERROR},
	{"INTERWORKING", SWITCH_CAUSE_INTERWORKING},
	{"SUCCESS", SWITCH_CAUSE_SUCCESS},
	{"ORIGINATOR_CANCEL", SWITCH_CAUSE_ORIGINATOR_CANCEL},
	{"CRASH", SWITCH_CAUSE_CRASH},
	{"SYSTEM_SHUTDOWN", SWITCH_CAUSE_SYSTEM_SHUTDOWN},
	{"LOSE_RACE", SWITCH_CAUSE_LOSE_RACE},
	{"MANAGER_REQUEST", SWITCH_CAUSE_MANAGER_REQUEST},
	{"BLIND_TRANSFER", SWITCH_CAUSE_BLIND_TRANSFER},
	{"ATTENDED_TRANSFER", SWITCH_CAUSE_ATTENDED_TRANSFER},
	{"ALLOTTED_TIMEOUT", SWITCH_CAUSE_ALLOTTED_TIMEOUT},
	{"USER_CHALLENGE", SWITCH_CAUSE_USER_CHALLENGE},
	{"MEDIA_TIMEOUT", SWITCH_CAUSE_MEDIA_TIMEOUT},
	{"PICKED_OFF", SWITCH_CAUSE_PICKED_OFF},
	{"USER_NOT_REGISTERED", SWITCH_CAUSE_USER_NOT_REGISTERED},
	{"PROGRESS_TIMEOUT", SWITCH_CAUSE_PROGRESS_TIMEOUT},
	{"INVALID_GATEWAY", SWITCH_CAUSE_INVALID_GATEWAY},
	{"GATEWAY_DOWN", SWITCH_CAUSE_GATEWAY_DOWN},
	{"INVALID_URL", SWITCH_CAUSE_INVALID_URL},
	{"INVALID_PROFILE", SWITCH_CAUSE_INVALID_PROFILE},
	{"NO_PICKUP", SWITCH_CAUSE_NO_PICKUP},
	{"SRTP_READ_ERROR", SWITCH_CAUSE_SRTP_READ_ERROR},
	{"BOWOUT", SWITCH_CAUSE_BOWOUT},
	{"BUSY_EVERYWHERE", SWITCH_CAUSE_BUSY_EVERYWHERE},
	{"DECLINE", SWITCH_CAUSE_DECLINE},
	{"DOES_NOT_EXIST_ANYWHERE", SWITCH_CAUSE_DOES_NOT_EXIST_ANYWHERE},
	{"NOT_ACCEPTABLE", SWITCH_CAUSE_NOT_ACCEPTABLE},
	{"UNWANTED", SWITCH_CAUSE_UNWANTED},
	{"NO_IDENTITY", SWITCH_CAUSE_NO_IDENTITY},
	{"BAD_IDENTITY_INFO", SWITCH_CAUSE_BAD_IDENTITY_INFO},
	{"UNSUPPORTED_CERTIFICATE", SWITCH_CAUSE_UNSUPPORTED_CERTIFICATE},
	{"INVALID_IDENTITY", SWITCH_CAUSE_INVALID_IDENTITY},
	{"STALE_DATE", SWITCH_CAUSE_STALE_DATE},
	{"REJECT_ALL", SWITCH_CAUSE_REJECT_ALL},
	{NULL, 0}
};

typedef enum {
	OCF_HANGUP = (1 << 0)
} opaque_channel_flag_t;

typedef enum {
	LP_NEITHER,
	LP_ORIGINATOR,
	LP_ORIGINATEE
} switch_originator_type_t;

struct switch_channel {
	char *name;
	switch_call_direction_t direction;
	switch_call_direction_t logical_direction;
	switch_queue_t *dtmf_queue;
	switch_queue_t *dtmf_log_queue;
	switch_mutex_t*dtmf_mutex;
	switch_mutex_t *flag_mutex;
	switch_mutex_t *state_mutex;
	switch_mutex_t *thread_mutex;
	switch_mutex_t *profile_mutex;
	switch_core_session_t *session;
	switch_channel_state_t state;
	switch_channel_state_t running_state;
	switch_channel_callstate_t callstate;
	uint32_t flags[CF_FLAG_MAX];
	uint32_t caps[CC_FLAG_MAX];
	uint8_t state_flags[CF_FLAG_MAX];
	uint32_t private_flags;
	switch_caller_profile_t *caller_profile;
	const switch_state_handler_table_t *state_handlers[SWITCH_MAX_STATE_HANDLERS];
	int state_handler_index;
	switch_event_t *variables;
	switch_event_t *scope_variables;
	switch_hash_t *private_hash;
	switch_hash_t *app_flag_hash;
	switch_call_cause_t hangup_cause;
	int vi;
	int event_count;
	int profile_index;
	opaque_channel_flag_t opaque_flags;
	switch_originator_type_t last_profile_type;
	switch_caller_extension_t *queued_extension;
	switch_event_t *app_list;
	switch_event_t *api_list;
	switch_event_t *var_list;
	switch_hold_record_t *hold_record;
	switch_device_node_t *device_node;
	char *device_id;
	switch_event_t *log_tags;
};

static void process_device_hup(switch_channel_t *channel);
static void switch_channel_check_device_state(switch_channel_t *channel, switch_channel_callstate_t callstate);

SWITCH_DECLARE(switch_hold_record_t *) switch_channel_get_hold_record(switch_channel_t *channel)
{
	return channel->hold_record;
}

SWITCH_DECLARE(const char *) switch_channel_cause2str(switch_call_cause_t cause)
{
	uint8_t x;
	const char *str = "UNKNOWN";

	for (x = 0; x < (sizeof(CAUSE_CHART) / sizeof(struct switch_cause_table)) - 1; x++) {
		if (CAUSE_CHART[x].cause == cause) {
			str = CAUSE_CHART[x].name;
			break;
		}
	}

	return str;
}

SWITCH_DECLARE(switch_call_cause_t) switch_channel_str2cause(const char *str)
{
	uint8_t x;
	switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING;

	if (!zstr(str)) {
		if (*str > 47 && *str < 58) {
			cause = atoi(str);
		} else {
			for (x = 0; x < (sizeof(CAUSE_CHART) / sizeof(struct switch_cause_table)) - 1 && CAUSE_CHART[x].name; x++) {
				if (!strcasecmp(CAUSE_CHART[x].name, str)) {
					cause = CAUSE_CHART[x].cause;
					break;
				}
			}
		}
	}

	return cause;
}

SWITCH_DECLARE(switch_call_cause_t) switch_channel_get_cause(switch_channel_t *channel)
{
	return channel->hangup_cause;
}


SWITCH_DECLARE(switch_call_cause_t *) switch_channel_get_cause_ptr(switch_channel_t *channel)
{
	return &channel->hangup_cause;
}


struct switch_callstate_table {
	const char *name;
	switch_channel_callstate_t callstate;
};
static struct switch_callstate_table CALLSTATE_CHART[] = {
	{"DOWN", CCS_DOWN},
	{"DIALING", CCS_DIALING},
	{"RINGING", CCS_RINGING},
	{"EARLY", CCS_EARLY},
	{"ACTIVE", CCS_ACTIVE},
	{"HELD", CCS_HELD},
	{"RING_WAIT", CCS_RING_WAIT},
	{"HANGUP", CCS_HANGUP},
	{"UNHELD", CCS_UNHELD},
	{NULL, 0}
};

struct switch_device_state_table {
	const char *name;
	switch_device_state_t device_state;
};
static struct switch_device_state_table DEVICE_STATE_CHART[] = {
	{"DOWN", SDS_DOWN},
	{"RINGING", SDS_RINGING},
	{"ACTIVE", SDS_ACTIVE},
	{"ACTIVE_MULTI", SDS_ACTIVE_MULTI},
	{"HELD", SDS_HELD},
	{"UNHELD", SDS_UNHELD},
	{"HANGUP", SDS_HANGUP},
	{NULL, 0}
};


SWITCH_DECLARE(void) switch_channel_perform_set_callstate(switch_channel_t *channel, switch_channel_callstate_t callstate,
														  const char *file, const char *func, int line)
{
	switch_event_t *event;
	switch_channel_callstate_t o_callstate = channel->callstate;

	if (o_callstate == callstate || o_callstate == CCS_HANGUP) return;

	channel->callstate = callstate;
	if (channel->device_node) {
		channel->device_node->callstate = callstate;
	}
	switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_DEBUG,
					  "(%s) Callstate Change %s -> %s\n", channel->name,
					  switch_channel_callstate2str(o_callstate), switch_channel_callstate2str(callstate));

	switch_channel_check_device_state(channel, channel->callstate);

	if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_CALLSTATE) == SWITCH_STATUS_SUCCESS) {
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Original-Channel-Call-State", switch_channel_callstate2str(o_callstate));
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Call-State-Number", "%d", callstate);
		switch_channel_event_set_data(channel, event);
		switch_event_fire(&event);
	}
}

SWITCH_DECLARE(switch_channel_callstate_t) switch_channel_get_callstate(switch_channel_t *channel)
{
	return channel->callstate;
}


SWITCH_DECLARE(const char *) switch_channel_callstate2str(switch_channel_callstate_t callstate)
{
	uint8_t x;
	const char *str = "UNKNOWN";

	for (x = 0; x < (sizeof(CALLSTATE_CHART) / sizeof(struct switch_cause_table)) - 1; x++) {
		if (CALLSTATE_CHART[x].callstate == callstate) {
			str = CALLSTATE_CHART[x].name;
			break;
		}
	}

	return str;
}

SWITCH_DECLARE(const char *) switch_channel_device_state2str(switch_device_state_t device_state)
{
	uint8_t x;
	const char *str = "UNKNOWN";

	for (x = 0; x < (sizeof(DEVICE_STATE_CHART) / sizeof(struct switch_cause_table)) - 1; x++) {
		if (DEVICE_STATE_CHART[x].device_state == device_state) {
			str = DEVICE_STATE_CHART[x].name;
			break;
		}
	}

	return str;
}


SWITCH_DECLARE(switch_channel_callstate_t) switch_channel_str2callstate(const char *str)
{
	uint8_t x;
	switch_channel_callstate_t callstate = (switch_channel_callstate_t) SWITCH_CAUSE_NONE;

	if (*str > 47 && *str < 58) {
		callstate = atoi(str);
	} else {
		for (x = 0; x < (sizeof(CALLSTATE_CHART) / sizeof(struct switch_callstate_table)) - 1 && CALLSTATE_CHART[x].name; x++) {
			if (!strcasecmp(CALLSTATE_CHART[x].name, str)) {
				callstate = CALLSTATE_CHART[x].callstate;
				break;
			}
		}
	}
	return callstate;
}



SWITCH_DECLARE(void) switch_channel_perform_audio_sync(switch_channel_t *channel, const char *file, const char *func, int line)
{
	if (switch_channel_media_up(channel)) {
		switch_core_session_message_t *msg = NULL;

		msg = switch_core_session_alloc(channel->session, sizeof(*msg));
		MESSAGE_STAMP_FFL(msg);
		msg->message_id = SWITCH_MESSAGE_INDICATE_AUDIO_SYNC;
		msg->from = channel->name;
		msg->_file = file;
		msg->_func = func;
		msg->_line = line;

		switch_core_session_queue_message(channel->session, msg);
	}
}


SWITCH_DECLARE(void) switch_channel_perform_video_sync(switch_channel_t *channel, const char *file, const char *func, int line)
{

	if (switch_channel_media_up(channel)) {
		switch_core_session_message_t *msg = NULL;

		msg = switch_core_session_alloc(channel->session, sizeof(*msg));
		MESSAGE_STAMP_FFL(msg);
		msg->message_id = SWITCH_MESSAGE_INDICATE_VIDEO_SYNC;
		msg->from = channel->name;
		msg->_file = file;
		msg->_func = func;
		msg->_line = line;

		switch_core_session_request_video_refresh(channel->session);
		switch_core_session_queue_message(channel->session, msg);
	}
}



SWITCH_DECLARE(switch_call_cause_t) switch_channel_cause_q850(switch_call_cause_t cause)
{
	if (cause <= SWITCH_CAUSE_INTERWORKING) {
		return cause;
	} else {
		return SWITCH_CAUSE_NORMAL_CLEARING;
	}
}

SWITCH_DECLARE(switch_call_cause_t) switch_channel_get_cause_q850(switch_channel_t *channel)
{
	return switch_channel_cause_q850(channel->hangup_cause);
}

SWITCH_DECLARE(switch_channel_timetable_t *) switch_channel_get_timetable(switch_channel_t *channel)
{
	switch_channel_timetable_t *times = NULL;

	if (channel->caller_profile) {
		switch_mutex_lock(channel->profile_mutex);
		times = channel->caller_profile->times;
		switch_mutex_unlock(channel->profile_mutex);
	}

	return times;
}

SWITCH_DECLARE(void) switch_channel_set_direction(switch_channel_t *channel, switch_call_direction_t direction)
{
	if (!switch_core_session_in_thread(channel->session)) {
		channel->direction = channel->logical_direction = direction;
		switch_channel_set_variable(channel, "direction", switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND ? "outbound" : "inbound");
	}
}

SWITCH_DECLARE(switch_call_direction_t) switch_channel_direction(switch_channel_t *channel)
{
	return channel->direction;
}

SWITCH_DECLARE(switch_call_direction_t) switch_channel_logical_direction(switch_channel_t *channel)
{
	return channel->logical_direction;
}

SWITCH_DECLARE(switch_status_t) switch_channel_alloc(switch_channel_t **channel, switch_call_direction_t direction, switch_memory_pool_t *pool)
{
	switch_assert(pool != NULL);

	if (((*channel) = switch_core_alloc(pool, sizeof(switch_channel_t))) == 0) {
		return SWITCH_STATUS_MEMERR;
	}

	switch_event_create_plain(&(*channel)->variables, SWITCH_EVENT_CHANNEL_DATA);

	switch_core_hash_init(&(*channel)->private_hash);
	switch_queue_create(&(*channel)->dtmf_queue, SWITCH_DTMF_LOG_LEN, pool);
	switch_queue_create(&(*channel)->dtmf_log_queue, SWITCH_DTMF_LOG_LEN, pool);

	switch_mutex_init(&(*channel)->dtmf_mutex, SWITCH_MUTEX_NESTED, pool);
	switch_mutex_init(&(*channel)->flag_mutex, SWITCH_MUTEX_NESTED, pool);
	switch_mutex_init(&(*channel)->state_mutex, SWITCH_MUTEX_NESTED, pool);
	switch_mutex_init(&(*channel)->thread_mutex, SWITCH_MUTEX_NESTED, pool);
	switch_mutex_init(&(*channel)->profile_mutex, SWITCH_MUTEX_NESTED, pool);
	(*channel)->hangup_cause = SWITCH_CAUSE_NONE;
	(*channel)->name = "";
	(*channel)->direction = (*channel)->logical_direction = direction;
	switch_channel_set_variable(*channel, "direction", switch_channel_direction(*channel) == SWITCH_CALL_DIRECTION_OUTBOUND ? "outbound" : "inbound");

	return SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE(switch_status_t) switch_channel_dtmf_lock(switch_channel_t *channel)
{
	return switch_mutex_lock(channel->dtmf_mutex);
}

SWITCH_DECLARE(switch_status_t) switch_channel_try_dtmf_lock(switch_channel_t *channel)
{
	return switch_mutex_trylock(channel->dtmf_mutex);
}

SWITCH_DECLARE(switch_status_t) switch_channel_dtmf_unlock(switch_channel_t *channel)
{
	return switch_mutex_unlock(channel->dtmf_mutex);
}

SWITCH_DECLARE(switch_size_t) switch_channel_has_dtmf(switch_channel_t *channel)
{
	switch_size_t has;

	switch_mutex_lock(channel->dtmf_mutex);
	has = switch_queue_size(channel->dtmf_queue);
	switch_mutex_unlock(channel->dtmf_mutex);

	return has;
}

SWITCH_DECLARE(switch_status_t) switch_channel_queue_dtmf(switch_channel_t *channel, const switch_dtmf_t *dtmf)
{
	switch_status_t status;
	void *pop;
	switch_dtmf_t new_dtmf = { 0 };
	switch_bool_t sensitive = switch_true(switch_channel_get_variable_dup(channel, SWITCH_SENSITIVE_DTMF_VARIABLE, SWITCH_FALSE, -1));

	switch_assert(dtmf);

	switch_mutex_lock(channel->dtmf_mutex);
	new_dtmf = *dtmf;

	if (sensitive) {
		switch_set_flag((&new_dtmf), DTMF_FLAG_SENSITIVE);
	}

	if ((status = switch_core_session_recv_dtmf(channel->session, dtmf) != SWITCH_STATUS_SUCCESS)) {
		goto done;
	}

	if (is_dtmf(new_dtmf.digit)) {
		switch_dtmf_t *dt;
		int x = 0;

		if (!sensitive) {
			switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_INFO, "RECV DTMF %c:%d\n", new_dtmf.digit, new_dtmf.duration);
		}

		if (new_dtmf.digit != 'w' && new_dtmf.digit != 'W') {
			if (new_dtmf.duration > switch_core_max_dtmf_duration(0)) {
				switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "%s EXCESSIVE DTMF DIGIT LEN [%d]\n",
								  switch_channel_get_name(channel), new_dtmf.duration);
				new_dtmf.duration = switch_core_max_dtmf_duration(0);
			} else if (new_dtmf.duration < switch_core_min_dtmf_duration(0)) {
				switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "%s SHORT DTMF DIGIT LEN [%d]\n",
								  switch_channel_get_name(channel), new_dtmf.duration);
				new_dtmf.duration = switch_core_min_dtmf_duration(0);
			}
		}

		if (!new_dtmf.duration) {
			new_dtmf.duration = switch_core_default_dtmf_duration(0);
		}


		switch_zmalloc(dt, sizeof(*dt));
		*dt = new_dtmf;


		while (switch_queue_trypush(channel->dtmf_queue, dt) != SWITCH_STATUS_SUCCESS) {
			if (switch_queue_trypop(channel->dtmf_queue, &pop) == SWITCH_STATUS_SUCCESS) {
				free(pop);
			}
			if (++x > 100) {
				status = SWITCH_STATUS_FALSE;
				free(dt);
				goto done;
			}
		}
	}

	status = SWITCH_STATUS_SUCCESS;

  done:

	switch_mutex_unlock(channel->dtmf_mutex);

	switch_core_media_break(channel->session, SWITCH_MEDIA_TYPE_AUDIO);

	return status;
}

SWITCH_DECLARE(switch_status_t) switch_channel_queue_dtmf_string(switch_channel_t *channel, const char *dtmf_string)
{
	char *p;
	switch_dtmf_t dtmf = { 0, switch_core_default_dtmf_duration(0), 0, SWITCH_DTMF_APP };
	int sent = 0, dur, bad_input = 0;
	char *string;
	int i, argc;
	char *argv[256];

	if (zstr(dtmf_string)) {
		return SWITCH_STATUS_GENERR;
	}


	dtmf.flags = DTMF_FLAG_SKIP_PROCESS;

	if (*dtmf_string == '~') {
		dtmf_string++;
		dtmf.flags = 0;
	}

	string = switch_core_session_strdup(channel->session, dtmf_string);
	argc = switch_separate_string(string, '+', argv, (sizeof(argv) / sizeof(argv[0])));

	for (i = 0; i < argc; i++) {
		dtmf.duration = switch_core_default_dtmf_duration(0);
		dur = switch_core_default_dtmf_duration(0) / 8;
		if ((p = strchr(argv[i], '@'))) {
			*p++ = '\0';
			if ((dur = atoi(p)) > (int)switch_core_min_dtmf_duration(0) / 8) {
				dtmf.duration = dur * 8;
			}
		}

		for (p = argv[i]; p && *p; p++) {
			if (is_dtmf(*p)) {
				dtmf.digit = *p;

				if (dtmf.duration > switch_core_max_dtmf_duration(0)) {
					switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_WARNING, "EXCESSIVE DTMF DIGIT LEN %c %d\n", dtmf.digit, dtmf.duration);
					dtmf.duration = switch_core_max_dtmf_duration(0);
				} else if (dtmf.duration < switch_core_min_dtmf_duration(0)) {
					switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_WARNING, "SHORT DTMF DIGIT LEN %c %d\n", dtmf.digit, dtmf.duration);
					dtmf.duration = switch_core_min_dtmf_duration(0);
				} else if (!dtmf.duration) {
					dtmf.duration = switch_core_default_dtmf_duration(0);
				}

				if (switch_channel_queue_dtmf(channel, &dtmf) == SWITCH_STATUS_SUCCESS) {
					switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "%s Queue dtmf\ndigit=%c ms=%u samples=%u\n",
									  switch_channel_get_name(channel), dtmf.digit, dur, dtmf.duration);
					sent++;
				}
			} else {
				bad_input++;
			}
		}

	}
	if (sent) {
		return SWITCH_STATUS_SUCCESS;
	}
	return bad_input ? SWITCH_STATUS_GENERR : SWITCH_STATUS_FALSE;
}

SWITCH_DECLARE(switch_status_t) switch_channel_dequeue_dtmf(switch_channel_t *channel, switch_dtmf_t *dtmf)
{
	switch_event_t *event;
	void *pop;
	switch_dtmf_t *dt;
	switch_status_t status = SWITCH_STATUS_FALSE;
	int sensitive = 0;

	switch_mutex_lock(channel->dtmf_mutex);

	if (switch_queue_trypop(channel->dtmf_queue, &pop) == SWITCH_STATUS_SUCCESS) {
		dt = (switch_dtmf_t *) pop;
		*dtmf = *dt;
		sensitive = switch_test_flag(dtmf, DTMF_FLAG_SENSITIVE);

		if (!sensitive && switch_queue_trypush(channel->dtmf_log_queue, dt) != SWITCH_STATUS_SUCCESS) {
			free(dt);
		}

		dt = NULL;

		if (dtmf->duration > switch_core_max_dtmf_duration(0)) {
			switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_WARNING, "%s EXCESSIVE DTMF DIGIT [%c] LEN [%d]\n",
							  switch_channel_get_name(channel), sensitive ? 'S' : dtmf->digit, dtmf->duration);
			dtmf->duration = switch_core_max_dtmf_duration(0);
		} else if (dtmf->duration < switch_core_min_dtmf_duration(0)) {
			switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_WARNING, "%s SHORT DTMF DIGIT [%c] LEN [%d]\n",
							  switch_channel_get_name(channel), sensitive ? 'S' : dtmf->digit, dtmf->duration);
			dtmf->duration = switch_core_min_dtmf_duration(0);
		} else if (!dtmf->duration) {
			dtmf->duration = switch_core_default_dtmf_duration(0);
		}

		status = SWITCH_STATUS_SUCCESS;
	}
	switch_mutex_unlock(channel->dtmf_mutex);

	if (!sensitive && status == SWITCH_STATUS_SUCCESS && switch_event_create(&event, SWITCH_EVENT_DTMF) == SWITCH_STATUS_SUCCESS) {
		const char *dtmf_source_str = NULL;
		switch_channel_event_set_data(channel, event);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "DTMF-Digit", "%c", dtmf->digit);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "DTMF-Duration", "%u", dtmf->duration);
		switch(dtmf->source) {
			case SWITCH_DTMF_INBAND_AUDIO:	/* From audio */
				dtmf_source_str = "INBAND_AUDIO";
				break;
			case SWITCH_DTMF_RTP:			/* From RTP as a telephone event */
				dtmf_source_str = "RTP";
				break;
			case SWITCH_DTMF_ENDPOINT:		/* From endpoint signaling */
				dtmf_source_str = "ENDPOINT";
				break;
			case SWITCH_DTMF_APP:			/* Injected by application */
				dtmf_source_str = "APP";
				break;
			case SWITCH_DTMF_UNKNOWN:		/* Unknown source */
			default:
				dtmf_source_str = "UNKNOWN";
				break;
		}
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "DTMF-Source", "%s", dtmf_source_str);
		if (switch_channel_test_flag(channel, CF_DIVERT_EVENTS)) {
			switch_core_session_queue_event(channel->session, &event);
		} else {
			switch_event_fire(&event);
		}
	}

	return status;
}

SWITCH_DECLARE(switch_size_t) switch_channel_dequeue_dtmf_string(switch_channel_t *channel, char *dtmf_str, switch_size_t len)
{
	switch_size_t x = 0;
	switch_dtmf_t dtmf = { 0 };

	memset(dtmf_str, 0, len);

	while (x < len - 1 && switch_channel_dequeue_dtmf(channel, &dtmf) == SWITCH_STATUS_SUCCESS) {
		dtmf_str[x++] = dtmf.digit;
	}

	return x;

}

SWITCH_DECLARE(void) switch_channel_flush_dtmf(switch_channel_t *channel)
{
	void *pop;

	switch_mutex_lock(channel->dtmf_mutex);
	while (switch_queue_trypop(channel->dtmf_queue, &pop) == SWITCH_STATUS_SUCCESS) {
		switch_dtmf_t *dt = (switch_dtmf_t *) pop;
		if (channel->state >= CS_HANGUP || switch_queue_trypush(channel->dtmf_log_queue, dt) != SWITCH_STATUS_SUCCESS) {
			free(dt);
		}
	}
	switch_mutex_unlock(channel->dtmf_mutex);
}

SWITCH_DECLARE(void) switch_channel_uninit(switch_channel_t *channel)
{
	void *pop;
	switch_channel_flush_dtmf(channel);
	while (switch_queue_trypop(channel->dtmf_log_queue, &pop) == SWITCH_STATUS_SUCCESS) {
		switch_safe_free(pop);
	}

	if (channel->private_hash) {
		switch_core_hash_destroy(&channel->private_hash);
	}

	if (channel->app_flag_hash) {
		switch_core_hash_destroy(&channel->app_flag_hash);
	}

	switch_mutex_lock(channel->profile_mutex);
	switch_event_destroy(&channel->variables);
	switch_event_destroy(&channel->api_list);
	switch_event_destroy(&channel->var_list);
	switch_event_destroy(&channel->app_list);
	if (channel->log_tags) {
		switch_event_destroy(&channel->log_tags);
	}
	switch_mutex_unlock(channel->profile_mutex);
}

SWITCH_DECLARE(switch_status_t) switch_channel_init(switch_channel_t *channel, switch_core_session_t *session, switch_channel_state_t state,
													switch_channel_flag_t flag)
{
	switch_assert(channel != NULL);
	channel->state = state;
	switch_channel_set_flag(channel, flag);
	channel->session = session;
	channel->running_state = CS_NONE;
	return SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE(void) switch_channel_perform_presence(switch_channel_t *channel, const char *rpid, const char *status, const char *id,
													 const char *file, const char *func, int line)
{
	switch_event_t *event;
	switch_event_types_t type = SWITCH_EVENT_PRESENCE_IN;
	const char *call_info = NULL;
	char *call_info_state = "active";

	if (switch_channel_test_flag(channel, CF_NO_PRESENCE)) {
		return;
	}

	if (!status) {
		type = SWITCH_EVENT_PRESENCE_OUT;
		status = "idle";
	}

	if (!id) {
		id = switch_channel_get_variable(channel, "presence_id");
	}

	if (!id) {
		return;
	}

	call_info = switch_channel_get_variable(channel, "presence_call_info");

	if (switch_event_create(&event, type) == SWITCH_STATUS_SUCCESS) {
		switch_channel_event_set_data(channel, event);
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", "any");
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", __FILE__);
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", id);
		if (type == SWITCH_EVENT_PRESENCE_IN) {
			if (!rpid) {
				rpid = "unknown";
			}
			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", rpid);
			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "status", status);
		}
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");


		if (!strcasecmp(status, "idle") || !switch_channel_up_nosig(channel)) {
			call_info_state = "idle";
		} else if (!strcasecmp(status, "hold-private")) {
			call_info_state = "held-private";
		} else if (!strcasecmp(status, "hold")) {
			call_info_state = "held";
		} else if (!switch_channel_test_flag(channel, CF_ANSWERED)) {
			if (channel->direction == SWITCH_CALL_DIRECTION_OUTBOUND) {
				call_info_state = "progressing";
			} else {
				if (switch_channel_test_flag(channel, CF_SLA_INTERCEPT)) {
					call_info_state = "idle";
				} else {
					call_info_state = "alerting";
				}
			}
		}

		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-info-state", call_info_state);

		if (call_info) {
			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-info", call_info);
		}

		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction",
									   channel->direction == SWITCH_CALL_DIRECTION_OUTBOUND ? "outbound" : "inbound");

		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", channel->event_count++);
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Presence-Calling-File", file);
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Presence-Calling-Function", func);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Presence-Calling-Line", "%d", line);

		if (switch_true(switch_channel_get_variable(channel, "presence_privacy"))) {
			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Presence-Privacy", "true");
		}

		switch_event_fire(&event);
	}
}

SWITCH_DECLARE(void) switch_channel_mark_hold(switch_channel_t *channel, switch_bool_t on)
{
	switch_event_t *event;

	if (!!on == !!switch_channel_test_flag(channel, CF_LEG_HOLDING)) {
		goto end;
	}

	if (on) {
		switch_channel_set_flag(channel, CF_LEG_HOLDING);
	} else {
		switch_channel_clear_flag(channel, CF_LEG_HOLDING);
	}

	if (switch_event_create(&event, on ? SWITCH_EVENT_CHANNEL_HOLD : SWITCH_EVENT_CHANNEL_UNHOLD) == SWITCH_STATUS_SUCCESS) {
		switch_channel_event_set_data(channel, event);
		switch_event_fire(&event);
	}

 end:

	if (on) {
		if (switch_true(switch_channel_get_variable(channel, "flip_record_on_hold"))) {
			switch_core_session_t *other_session;
			if (switch_core_session_get_partner(channel->session, &other_session) == SWITCH_STATUS_SUCCESS) {
				switch_ivr_transfer_recordings(channel->session, other_session);
				switch_core_session_rwunlock(other_session);
			}
		}
	}

}

SWITCH_DECLARE(const char *) switch_channel_get_hold_music(switch_channel_t *channel)
{
	const char *var;

	if (!(var = switch_channel_get_variable(channel, SWITCH_TEMP_HOLD_MUSIC_VARIABLE))) {
		var = switch_channel_get_variable(channel, SWITCH_HOLD_MUSIC_VARIABLE);
	}

	if (!zstr(var)) {
		char *expanded = switch_channel_expand_variables(channel, var);

		if (expanded != var) {
			var = switch_core_session_strdup(channel->session, expanded);
			free(expanded);
		}
	}


	return var;
}

SWITCH_DECLARE(const char *) switch_channel_get_hold_music_partner(switch_channel_t *channel)
{
	switch_core_session_t *session;
	const char *r = NULL;

	if (switch_core_session_get_partner(channel->session, &session) == SWITCH_STATUS_SUCCESS) {
		r = switch_channel_get_hold_music(switch_core_session_get_channel(session));
		switch_core_session_rwunlock(session);
	}

	return r;
}

SWITCH_DECLARE(void) switch_channel_set_scope_variables(switch_channel_t *channel, switch_event_t **event)
{
	switch_mutex_lock(channel->profile_mutex);

	if (event && *event) { /* push */
		(*event)->next = channel->scope_variables;
		channel->scope_variables = *event;
		*event = NULL;
	} else if (channel->scope_variables) { /* pop */
		switch_event_t *top_event = channel->scope_variables;
		channel->scope_variables = channel->scope_variables->next;
		switch_event_destroy(&top_event);
	}

	switch_mutex_unlock(channel->profile_mutex);

}

SWITCH_DECLARE(switch_status_t) switch_channel_get_scope_variables(switch_channel_t *channel, switch_event_t **event)
{
	switch_status_t status = SWITCH_STATUS_FALSE;
	switch_event_t *new_event;

	switch_mutex_lock(channel->profile_mutex);
	if (channel->scope_variables) {
		switch_event_t *ep;
		switch_event_header_t *hp;

		switch_event_create_plain(&new_event, SWITCH_EVENT_CHANNEL_DATA);
		status = SWITCH_STATUS_SUCCESS;
		*event = new_event;

		for (ep = channel->scope_variables; ep; ep = ep->next) {
			for (hp = ep->headers; hp; hp = hp->next) {
				if (!switch_event_get_header(new_event, hp->value)) {
					switch_event_add_header_string(new_event, SWITCH_STACK_BOTTOM, hp->name, hp->value);
				}
			}
		}
	}
	switch_mutex_unlock(channel->profile_mutex);

	return status;
}

SWITCH_DECLARE(const char *) switch_channel_get_variable_dup(switch_channel_t *channel, const char *varname, switch_bool_t dup, int idx)
{
	const char *v = NULL, *r = NULL, *vdup = NULL;
	switch_assert(channel != NULL);

	switch_mutex_lock(channel->profile_mutex);

	if (!zstr(varname)) {
		if (channel->scope_variables) {
			switch_event_t *ep;

			for (ep = channel->scope_variables; ep; ep = ep->next) {
				if ((v = switch_event_get_header_idx(ep, varname, idx))) {
					break;
				}
			}
		}

		if (!v && (!channel->variables || !(v = switch_event_get_header_idx(channel->variables, varname, idx)))) {
			switch_caller_profile_t *cp = switch_channel_get_caller_profile(channel);

			if (cp) {
				if (!strncmp(varname, "aleg_", 5)) {
					cp = cp->originator_caller_profile;
					varname += 5;
				} else if (!strncmp(varname, "bleg_", 5)) {
					cp = cp->originatee_caller_profile;
					varname += 5;
				}
			}

			if (!cp || !(v = switch_caller_get_field_by_name(cp, varname))) {
				if ((vdup = switch_core_get_variable_pdup(varname, switch_core_session_get_pool(channel->session)))) {
					v = vdup;
				}
			}
		}
	}

	if (dup && v != vdup) {
		if (v) {
			r = switch_core_session_strdup(channel->session, v);
		}
	} else {
		r = v;
	}

	switch_mutex_unlock(channel->profile_mutex);

	return r;
}

SWITCH_DECLARE(const char *) switch_channel_get_variable_strdup(switch_channel_t *channel, const char *varname)
{
	const char *value = switch_channel_get_variable_dup(channel, varname, SWITCH_FALSE, -1);

	return value ? (const char *)strdup(value) : NULL;
}

SWITCH_DECLARE(switch_status_t) switch_channel_get_variable_buf(switch_channel_t *channel, const char *varname, char *buf, switch_size_t buflen)
{
	const char *value = switch_channel_get_variable_dup(channel, varname, SWITCH_FALSE, -1);

	if (value && buf && buflen && switch_copy_string(buf, value, buflen)) {
		return SWITCH_STATUS_SUCCESS;
	}

	return SWITCH_STATUS_FALSE;
}

SWITCH_DECLARE(const char *) switch_channel_get_variable_partner(switch_channel_t *channel, const char *varname)
{
	const char *uuid;
	const char *val = NULL, *r = NULL;
	switch_assert(channel != NULL);

	if (!zstr(varname)) {
		if ((uuid = switch_channel_get_partner_uuid(channel))) {
			switch_core_session_t *session;
			if ((session = switch_core_session_locate(uuid))) {
				switch_channel_t *tchannel = switch_core_session_get_channel(session);
				val = switch_channel_get_variable(tchannel, varname);
				switch_core_session_rwunlock(session);
			}
		}
	}

	if (val)
		r = switch_core_session_strdup(channel->session, val);

	return r;
}


SWITCH_DECLARE(void) switch_channel_variable_last(switch_channel_t *channel)
{
	switch_assert(channel != NULL);
	if (!channel->vi) {
		return;
	}
	channel->vi = 0;
	switch_mutex_unlock(channel->profile_mutex);

}

SWITCH_DECLARE(switch_event_header_t *) switch_channel_variable_first(switch_channel_t *channel)
{
	switch_event_header_t *hi = NULL;

	switch_assert(channel != NULL);
	switch_mutex_lock(channel->profile_mutex);
	if (channel->variables && (hi = channel->variables->headers)) {
		channel->vi = 1;
	} else {
		switch_mutex_unlock(channel->profile_mutex);
	}

	return hi;
}

SWITCH_DECLARE(switch_status_t) switch_channel_set_private(switch_channel_t *channel, const char *key, const void *private_info)
{
	switch_assert(channel != NULL);
	switch_core_hash_insert_locked(channel->private_hash, key, private_info, channel->profile_mutex);
	return SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE(void *) switch_channel_get_private(switch_channel_t *channel, const char *key)
{
	void *val;
	switch_assert(channel != NULL);
	val = switch_core_hash_find_locked(channel->private_hash, key, channel->profile_mutex);
	return val;
}

SWITCH_DECLARE(void *) switch_channel_get_private_partner(switch_channel_t *channel, const char *key)
{
	const char *uuid;
	void *val = NULL;

	switch_assert(channel != NULL);

	if ((uuid = switch_channel_get_partner_uuid(channel))) {
		switch_core_session_t *session;
		if ((session = switch_core_session_locate(uuid))) {
			val = switch_core_hash_find_locked(channel->private_hash, key, channel->profile_mutex);
			switch_core_session_rwunlock(session);
		}
	}

	return val;
}

SWITCH_DECLARE(switch_status_t) switch_channel_set_name(switch_channel_t *channel, const char *name)
{
	const char *old = NULL;

	switch_assert(channel != NULL);
	if (!zstr(channel->name)) {
		old = channel->name;
	}
	channel->name = NULL;
	if (name) {
		char *uuid = switch_core_session_get_uuid(channel->session);
		channel->name = switch_core_session_strdup(channel->session, name);
		switch_channel_set_variable(channel, SWITCH_CHANNEL_NAME_VARIABLE, name);
		if (old) {
			switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_NOTICE, "Rename Channel %s->%s [%s]\n", old, name, uuid);
		} else {
			switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_NOTICE, "New Channel %s [%s]\n", name, uuid);
		}
	}
	return SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE(char *) switch_channel_get_name(switch_channel_t *channel)
{
	switch_assert(channel != NULL);
	return (!zstr(channel->name)) ? channel->name : "N/A";
}

SWITCH_DECLARE(switch_status_t) switch_channel_set_profile_var(switch_channel_t *channel, const char *name, const char *val)
{
	char *v;
	switch_status_t status = SWITCH_STATUS_SUCCESS;

	switch_mutex_lock(channel->profile_mutex);


	if (!strcasecmp(name, "device_id") && !zstr(val)) {
		const char *device_id;
		if (!(device_id = switch_channel_set_device_id(channel, val))) {
			/* one time setting */
			switch_mutex_unlock(channel->profile_mutex);
			return status;
		}

		val = device_id;
	}

	if (!zstr(val)) {
		v = switch_core_strdup(channel->caller_profile->pool, val);
	} else {
		v = SWITCH_BLANK_STRING;
	}

	if (!strcasecmp(name, "dialplan")) {
		channel->caller_profile->dialplan = v;
	} else if (!strcasecmp(name, "username")) {
		channel->caller_profile->username = v;
	} else if (!strcasecmp(name, "caller_id_name")) {
		channel->caller_profile->caller_id_name = v;
	} else if (!strcasecmp(name, "caller_id_number")) {
		channel->caller_profile->caller_id_number = v;
	} else if (!strcasecmp(name, "callee_id_name")) {
		channel->caller_profile->callee_id_name = v;
	} else if (!strcasecmp(name, "callee_id_number")) {
		channel->caller_profile->callee_id_number = v;
	} else if (val && !strcasecmp(name, "caller_ton")) {
		channel->caller_profile->caller_ton = (uint8_t) atoi(v);
	} else if (val && !strcasecmp(name, "caller_numplan")) {
		channel->caller_profile->caller_numplan = (uint8_t) atoi(v);
	} else if (val && !strcasecmp(name, "destination_number_ton")) {
		channel->caller_profile->destination_number_ton = (uint8_t) atoi(v);
	} else if (val && !strcasecmp(name, "destination_number_numplan")) {
		channel->caller_profile->destination_number_numplan = (uint8_t) atoi(v);
	} else if (!strcasecmp(name, "ani")) {
		channel->caller_profile->ani = v;
	} else if (!strcasecmp(name, "aniii")) {
		channel->caller_profile->aniii = v;
	} else if (!strcasecmp(name, "network_addr")) {
		channel->caller_profile->network_addr = v;
	} else if (!strcasecmp(name, "rdnis")) {
		channel->caller_profile->rdnis = v;
	} else if (!strcasecmp(name, "destination_number")) {
		channel->caller_profile->destination_number = v;
	} else if (!strcasecmp(name, "uuid")) {
		channel->caller_profile->uuid = v;
	} else if (!strcasecmp(name, "source")) {
		channel->caller_profile->source = v;
	} else if (!strcasecmp(name, "context")) {
		channel->caller_profile->context = v;
	} else if (!strcasecmp(name, "chan_name")) {
		channel->caller_profile->chan_name = v;
	} else {
		profile_node_t *pn, *n = switch_core_alloc(channel->caller_profile->pool, sizeof(*n));
		int var_found;

		n->var = switch_core_strdup(channel->caller_profile->pool, name);
		n->val = v;

		if (!channel->caller_profile->soft) {
			channel->caller_profile->soft = n;
		} else {
			var_found = 0;

			for(pn = channel->caller_profile->soft; pn ; pn = pn->next) {
				if (!strcasecmp(pn->var,n->var)) {
					pn->val = n->val;
					var_found = 1;
					break;
				}

				if(!pn->next) {
					break;
				}
			}

			if (pn && !pn->next && !var_found) {
				pn->next = n;
			}
		}
	}
	switch_mutex_unlock(channel->profile_mutex);

	return status;
}


SWITCH_DECLARE(void) switch_channel_process_export(switch_channel_t *channel, switch_channel_t *peer_channel,
												   switch_event_t *var_event, const char *export_varname)
{

	const char *export_vars = switch_channel_get_variable(channel, export_varname);
	char *cptmp = switch_core_session_strdup(channel->session, export_vars);
	int argc;
	char *argv[256];

	if (zstr(export_vars)) return;


	if (var_event) {
		switch_event_del_header(var_event, export_varname);
		switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, export_varname, export_vars);
	}

	if (peer_channel) {
		switch_channel_set_variable(peer_channel, export_varname, export_vars);
	}

	if ((argc = switch_separate_string(cptmp, ',', argv, (sizeof(argv) / sizeof(argv[0]))))) {
		int x;

		for (x = 0; x < argc; x++) {
			const char *vval;
			if ((vval = switch_channel_get_variable(channel, argv[x]))) {
				char *vvar = argv[x];
				if (!strncasecmp(vvar, "nolocal:", 8)) { /* remove this later ? */
					vvar += 8;
				} else if (!strncasecmp(vvar, "_nolocal_", 9)) {
					vvar += 9;
				}
				if (var_event) {
					switch_event_del_header(var_event, vvar);
					switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, vvar, vval);
					switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(channel->session), SWITCH_LOG_DEBUG,
									  "%s EXPORTING[%s] [%s]=[%s] to event\n",
									  switch_channel_get_name(channel),
									  export_varname,
									  vvar, vval);
				}
				if (peer_channel) {
					switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(channel->session), SWITCH_LOG_DEBUG,
									  "%s EXPORTING[%s] [%s]=[%s] to %s\n",
									  switch_channel_get_name(channel),
									  export_varname,
									  vvar, vval, switch_channel_get_name(peer_channel));
					switch_channel_set_variable(peer_channel, vvar, vval);
				}
			}
		}
	}


}

SWITCH_DECLARE(switch_status_t) switch_channel_export_variable_var_check(switch_channel_t *channel,
																		 const char *varname, const char *val,
																		 const char *export_varname, switch_bool_t var_check)
{
	char *var_name = NULL;
	const char *exports;
	char *var, *new_exports, *new_exports_d = NULL;
	int local = 1;

	exports = switch_channel_get_variable(channel, export_varname);

	var = switch_core_session_strdup(channel->session, varname);

	if (var) {
		if (!strncasecmp(var, "nolocal:", 8)) { /* remove this later ? */
			var_name = var + 8;
			local = 0;
		} else if (!strncasecmp(var, "_nolocal_", 9)) {
			var_name = var + 9;
			local = 0;
		} else {
			var_name = var;
		}
	}

	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(channel->session), SWITCH_LOG_DEBUG, "EXPORT (%s) %s[%s]=[%s]\n",
					  export_varname, local ? "" : "(REMOTE ONLY) ",
					  var_name ? var_name : "", val ? val : "UNDEF");


	switch_channel_set_variable_var_check(channel, var, val, var_check);

	if (var && val) {
		if (exports) {
			new_exports_d = switch_mprintf("%s,%s", exports, var);
			new_exports = new_exports_d;
		} else {
			new_exports = var;
		}

		switch_channel_set_variable(channel, export_varname, new_exports);

		switch_safe_free(new_exports_d);
	}

	return SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE(switch_status_t) switch_channel_export_variable_printf(switch_channel_t *channel, const char *varname,
																	  const char *export_varname, const char *fmt, ...)
{
	switch_status_t status = SWITCH_STATUS_FALSE;
	char *data = NULL;
	va_list ap;
	int ret;

	switch_assert(channel != NULL);

	va_start(ap, fmt);
	ret = switch_vasprintf(&data, fmt, ap);
	va_end(ap);

	if (ret == -1) {
		return SWITCH_STATUS_FALSE;
	}

	status = switch_channel_export_variable(channel, varname, data, export_varname);

	free(data);

	return status;
}


SWITCH_DECLARE(uint32_t) switch_channel_del_variable_prefix(switch_channel_t *channel, const char *prefix)
{
	switch_event_t *event;
	switch_event_header_t *hp;
	uint32_t r = 0;

	switch_channel_get_variables(channel, &event);

	if (event) {
		for (hp = event->headers; hp; hp = hp->next) {
			if (zstr(prefix) || !strncasecmp(hp->name, prefix, strlen(prefix))) {
				switch_channel_set_variable(channel, hp->name, NULL);
			}
		}
	}

	switch_event_destroy(&event);

	return r;
}

SWITCH_DECLARE(switch_status_t) switch_channel_transfer_variable_prefix(switch_channel_t *orig_channel, switch_channel_t *new_channel, const char *prefix)
{
	switch_event_header_t *hi = NULL;
	int x = 0;

	if ((hi = switch_channel_variable_first(orig_channel))) {
		for (; hi; hi = hi->next) {
			char *var = hi->name;
			char *val = hi->value;

			if (zstr(prefix) || !strncasecmp(var, prefix, strlen(prefix))) {
				x++;
				switch_channel_set_variable(new_channel, var, val);
			}
		}
		switch_channel_variable_last(orig_channel);
	}

	return x ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
}

SWITCH_DECLARE(void) switch_channel_set_presence_data_vals(switch_channel_t *channel, const char *presence_data_cols)
{
	char *cols[128] = { 0 };
	char header_name[128] = "";
	int col_count = 0, i = 0;
	char *data_copy = NULL;

	if (zstr(presence_data_cols)) {
		presence_data_cols = switch_channel_get_variable_dup(channel, "presence_data_cols", SWITCH_FALSE, -1);
		if (zstr(presence_data_cols)) {
			return;
		}
	}

	data_copy = strdup(presence_data_cols);

	col_count = switch_split(data_copy, ':', cols);

	for (i = 0; i < col_count; i++) {
		const char *val = NULL;
		switch_snprintf(header_name, sizeof(header_name), "PD-%s", cols[i]);
		val = switch_channel_get_variable(channel, cols[i]);
		switch_channel_set_profile_var(channel, header_name, val);
	}

	switch_safe_free(data_copy);
}

SWITCH_DECLARE(switch_status_t) switch_channel_set_log_tag(switch_channel_t *channel, const char *tagname, const char *tagvalue)
{
	switch_status_t status = SWITCH_STATUS_FALSE;
	switch_assert(channel != NULL);
	switch_mutex_lock(channel->profile_mutex);
	if (!zstr(tagname)) {
		if (!channel->log_tags) {
			switch_event_create_plain(&channel->log_tags, SWITCH_EVENT_CHANNEL_DATA);
		}
		if (zstr(tagvalue)) {
			switch_event_del_header(channel->log_tags, tagname);
		} else {
			switch_event_add_header_string(channel->log_tags, SWITCH_STACK_BOTTOM, tagname, tagvalue);
		}
		status = SWITCH_STATUS_SUCCESS;
	}
	switch_mutex_unlock(channel->profile_mutex);
	return status;
}

SWITCH_DECLARE(switch_status_t) switch_channel_get_log_tags(switch_channel_t *channel, switch_event_t **log_tags)
{
	switch_status_t status = SWITCH_STATUS_FALSE;
	switch_assert(channel != NULL);
	if (!channel->log_tags) {
		return status;
	}
	switch_mutex_lock(channel->profile_mutex);
	if (channel->log_tags && log_tags) {
		status = switch_event_dup(log_tags, channel->log_tags);
	}
	switch_mutex_unlock(channel->profile_mutex);
	return status;
}

SWITCH_DECLARE(switch_status_t) switch_channel_set_variable_var_check(switch_channel_t *channel,
																	  const char *varname, const char *value, switch_bool_t var_check)
{
	switch_status_t status = SWITCH_STATUS_FALSE;

	switch_assert(channel != NULL);

	switch_mutex_lock(channel->profile_mutex);
	if (channel->variables && !zstr(varname)) {
		if (zstr(value)) {
			switch_event_del_header(channel->variables, varname);
		} else {
			int ok = 1;

			if (var_check) {
				ok = !switch_string_var_check_const(value);
			}
			if (ok) {
				switch_event_add_header_string(channel->variables, SWITCH_STACK_BOTTOM, varname, value);
			} else {
				switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_CRIT, "Invalid data (${%s} contains a variable)\n", varname);
			}
		}
		status = SWITCH_STATUS_SUCCESS;
	}
	switch_mutex_unlock(channel->profile_mutex);

	return status;
}

SWITCH_DECLARE(switch_status_t) switch_channel_set_variable_strip_quotes_var_check(switch_channel_t *channel,
	const char *varname, const char *value, switch_bool_t var_check)
{
	switch_status_t status = SWITCH_STATUS_FALSE;

	switch_assert(channel != NULL);

	switch_mutex_lock(channel->profile_mutex);
	if (channel->variables && !zstr(varname)) {
		if (zstr(value)) {
			switch_event_del_header(channel->variables, varname);
		} else {
			int ok = 1;
			char *t = (char *)value;
			char *r = (char *)value;
			char *tmp = NULL;

			if (t && *t == '"') {
				t++;
				if (end_of(t) == '"') {
					r = tmp = strdup(t);
					switch_assert(r);
					end_of(r) = '\0';
				}
			}

			if (var_check) {
				ok = !switch_string_var_check_const(r);
			}
			if (ok) {
				switch_event_add_header_string(channel->variables, SWITCH_STACK_BOTTOM, varname, r);
			} else {
				switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_CRIT, "Invalid data (${%s} contains a variable)\n", varname);
			}

			switch_safe_free(tmp);
		}
		status = SWITCH_STATUS_SUCCESS;
	}
	switch_mutex_unlock(channel->profile_mutex);

	return status;
}

SWITCH_DECLARE(switch_status_t) switch_channel_add_variable_var_check(switch_channel_t *channel,
																	  const char *varname, const char *value, switch_bool_t var_check, switch_stack_t stack)
{
	switch_status_t status = SWITCH_STATUS_FALSE;

	switch_assert(channel != NULL);

	switch_mutex_lock(channel->profile_mutex);
	if (channel->variables && !zstr(varname)) {
		if (zstr(value)) {
			switch_event_del_header(channel->variables, varname);
		} else {
			int ok = 1;

			if (var_check) {
				ok = !switch_string_var_check_const(value);
			}
			if (ok) {
				switch_event_add_header_string(channel->variables, stack, varname, value);
			} else {
				switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_CRIT, "Invalid data (${%s} contains a variable)\n", varname);
			}
		}
		status = SWITCH_STATUS_SUCCESS;
	}
	switch_mutex_unlock(channel->profile_mutex);

	return status;
}


switch_status_t switch_event_base_add_header(switch_event_t *event, switch_stack_t stack, const char *header_name, char *data);

SWITCH_DECLARE(switch_status_t) switch_channel_set_variable_printf(switch_channel_t *channel, const char *varname, const char *fmt, ...)
{
	int ret = 0;
	char *data;
	va_list ap;
	switch_status_t status = SWITCH_STATUS_FALSE;

	switch_assert(channel != NULL);

	switch_mutex_lock(channel->profile_mutex);
	if (channel->variables && !zstr(varname)) {
		switch_event_del_header(channel->variables, varname);

		va_start(ap, fmt);
		ret = switch_vasprintf(&data, fmt, ap);
		va_end(ap);

		if (ret == -1) {
			switch_mutex_unlock(channel->profile_mutex);
			return SWITCH_STATUS_MEMERR;
		}

		status = switch_channel_set_variable(channel, varname, data);
		free(data);
	}
	switch_mutex_unlock(channel->profile_mutex);

	return status;
}


SWITCH_DECLARE(switch_status_t) switch_channel_set_variable_name_printf(switch_channel_t *channel, const char *val, const char *fmt, ...)
{
	int ret = 0;
	char *varname;
	va_list ap;
	switch_status_t status = SWITCH_STATUS_FALSE;

	switch_assert(channel != NULL);

	switch_mutex_lock(channel->profile_mutex);

	va_start(ap, fmt);
	ret = switch_vasprintf(&varname, fmt, ap);
	va_end(ap);

	if (ret == -1) {
		switch_mutex_unlock(channel->profile_mutex);
		return SWITCH_STATUS_MEMERR;
	}

	status = switch_channel_set_variable(channel, varname, val);

	free(varname);

	switch_mutex_unlock(channel->profile_mutex);

	return status;
}


SWITCH_DECLARE(switch_status_t) switch_channel_set_variable_partner_var_check(switch_channel_t *channel,
																			  const char *varname, const char *value, switch_bool_t var_check)
{
	const char *uuid;
	switch_assert(channel != NULL);

	if (!zstr(varname)) {
		if ((uuid = switch_channel_get_partner_uuid(channel))) {
			switch_core_session_t *session;
			if ((session = switch_core_session_locate(uuid))) {
				switch_channel_t *tchannel = switch_core_session_get_channel(session);
				switch_channel_set_variable_var_check(tchannel, varname, value, var_check);
				switch_core_session_rwunlock(session);
			}
			return SWITCH_STATUS_SUCCESS;
		}
	}

	return SWITCH_STATUS_FALSE;
}

SWITCH_DECLARE(uint32_t) switch_channel_test_flag(switch_channel_t *channel, switch_channel_flag_t flag)
{
	uint32_t r = 0;

	switch_assert(channel != NULL);

	switch_mutex_lock(channel->flag_mutex);
	r = channel->flags[flag];
	switch_mutex_unlock(channel->flag_mutex);

	return r;
}

SWITCH_DECLARE(switch_bool_t) switch_channel_set_flag_partner(switch_channel_t *channel, switch_channel_flag_t flag)
{
	const char *uuid;

	switch_assert(channel != NULL);

	if ((uuid = switch_channel_get_partner_uuid(channel))) {
		switch_core_session_t *session;
		if ((session = switch_core_session_locate(uuid))) {
			switch_channel_set_flag(switch_core_session_get_channel(session), flag);
			switch_core_session_rwunlock(session);
			return SWITCH_TRUE;
		}
	}

	return SWITCH_FALSE;
}

SWITCH_DECLARE(uint32_t) switch_channel_test_flag_partner(switch_channel_t *channel, switch_channel_flag_t flag)
{
	const char *uuid;
	int r = 0;

	switch_assert(channel != NULL);

	if ((uuid = switch_channel_get_partner_uuid(channel))) {
		switch_core_session_t *session;
		if ((session = switch_core_session_locate(uuid))) {
			r = switch_channel_test_flag(switch_core_session_get_channel(session), flag);
			switch_core_session_rwunlock(session);
		}
	}

	return r;
}

SWITCH_DECLARE(switch_bool_t) switch_channel_clear_flag_partner(switch_channel_t *channel, switch_channel_flag_t flag)
{
	const char *uuid;

	switch_assert(channel != NULL);

	if ((uuid = switch_channel_get_partner_uuid(channel))) {
		switch_core_session_t *session;
		if ((session = switch_core_session_locate(uuid))) {
			switch_channel_clear_flag(switch_core_session_get_channel(session), flag);
			switch_core_session_rwunlock(session);
			return SWITCH_TRUE;
		}
	}

	return SWITCH_FALSE;
}

SWITCH_DECLARE(void) switch_channel_wait_for_state(switch_channel_t *channel, switch_channel_t *other_channel, switch_channel_state_t want_state)
{

	switch_assert(channel);

	for (;;) {
		if ((channel->state < CS_HANGUP && channel->state == channel->running_state && channel->running_state == want_state) ||
			(other_channel && switch_channel_down_nosig(other_channel)) || switch_channel_down(channel)) {
			break;
		}
		switch_cond_next();
	}
}


SWITCH_DECLARE(void) switch_channel_wait_for_state_timeout(switch_channel_t *channel, switch_channel_state_t want_state, uint32_t timeout)
{

	uint32_t count = 0;

	for (;;) {

		if ((channel->state == channel->running_state && channel->running_state == want_state) || channel->state >= CS_HANGUP) {
			break;
		}

		switch_channel_check_signal(channel, SWITCH_TRUE);

		switch_cond_next();

		if (++count >= timeout) {
			break;
		}
	}
}

SWITCH_DECLARE(switch_status_t) switch_channel_wait_for_flag(switch_channel_t *channel,
															 switch_channel_flag_t want_flag,
															 switch_bool_t pres, uint32_t to, switch_channel_t *super_channel)
{

	if (to) {
		to++;
	}

	for (;;) {
		if (pres) {
			if (switch_channel_test_flag(channel, want_flag)) {
				break;
			}
		} else {
			if (!switch_channel_test_flag(channel, want_flag)) {
				break;
			}
		}

		switch_cond_next();

		if (super_channel && !switch_channel_ready(super_channel)) {
			return SWITCH_STATUS_FALSE;
		}

		if (switch_channel_down(channel)) {
			return SWITCH_STATUS_FALSE;
		}

		if (to && !--to) {
			return SWITCH_STATUS_TIMEOUT;
		}
	}

	return SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE(switch_status_t) switch_channel_wait_for_app_flag(switch_channel_t *channel,
																 uint32_t app_flag,
																 const char *key, switch_bool_t pres, uint32_t to)
{
	int r = 0;
	
	if (to) {
		to++;
	}

	for (;;) {
		if (pres) {
			if ((r = switch_channel_test_app_flag_key(key, channel, app_flag))) {
				break;
			}
		} else {
			if (!(r = switch_channel_test_app_flag_key(key, channel, app_flag))) {
				break;
			}
		}

		switch_cond_next();

		if (switch_channel_down(channel)) {
			return r;
		}

		if (to && !--to) {
			return r;
		}
	}

	return r;
}


SWITCH_DECLARE(void) switch_channel_set_cap_value(switch_channel_t *channel, switch_channel_cap_t cap, uint32_t value)
{
	switch_assert(channel);
	switch_assert(channel->flag_mutex);

	switch_mutex_lock(channel->flag_mutex);
	channel->caps[cap] = value;
	switch_mutex_unlock(channel->flag_mutex);
}

SWITCH_DECLARE(void) switch_channel_clear_cap(switch_channel_t *channel, switch_channel_cap_t cap)
{
	switch_assert(channel != NULL);
	switch_assert(channel->flag_mutex);

	switch_mutex_lock(channel->flag_mutex);
	channel->caps[cap] = 0;
	switch_mutex_unlock(channel->flag_mutex);
}

SWITCH_DECLARE(uint32_t) switch_channel_test_cap(switch_channel_t *channel, switch_channel_cap_t cap)
{
	switch_assert(channel != NULL);
	return channel->caps[cap] ? 1 : 0;
}

SWITCH_DECLARE(uint32_t) switch_channel_test_cap_partner(switch_channel_t *channel, switch_channel_cap_t cap)
{
	const char *uuid;
	int r = 0;

	switch_assert(channel != NULL);

	if ((uuid = switch_channel_get_partner_uuid(channel))) {
		switch_core_session_t *session;
		if ((session = switch_core_session_locate(uuid))) {
			r = switch_channel_test_cap(switch_core_session_get_channel(session), cap);
			switch_core_session_rwunlock(session);
		}
	}

	return r;
}

SWITCH_DECLARE(char *) switch_channel_get_flag_string(switch_channel_t *channel)
{
	switch_stream_handle_t stream = { 0 };
	char *r;
	int i = 0;

	SWITCH_STANDARD_STREAM(stream);

	switch_mutex_lock(channel->flag_mutex);
	for (i = 0; i < CF_FLAG_MAX; i++) {
		if (channel->flags[i]) {
			stream.write_function(&stream, "%d=%d;", i, channel->flags[i]);
		}
	}
	switch_mutex_unlock(channel->flag_mutex);

	r = (char *) stream.data;

	if (end_of(r) == ';') {
		end_of(r) = '\0';
	}

	return r;

}

SWITCH_DECLARE(char *) switch_channel_get_cap_string(switch_channel_t *channel)
{
	switch_stream_handle_t stream = { 0 };
	char *r;
	int i = 0;

	SWITCH_STANDARD_STREAM(stream);

	switch_mutex_lock(channel->flag_mutex);
	for (i = 0; i < CC_FLAG_MAX; i++) {
		if (channel->caps[i]) {
			stream.write_function(&stream, "%d=%d;", i, channel->caps[i]);
		}
	}
	switch_mutex_unlock(channel->flag_mutex);

	r = (char *) stream.data;

	if (end_of(r) == ';') {
		end_of(r) = '\0';
	}

	return r;

}

SWITCH_DECLARE(void) switch_channel_set_flag_value(switch_channel_t *channel, switch_channel_flag_t flag, uint32_t value)
{
	int HELD = 0;
	int just_set = 0;

	switch_assert(channel);
	switch_assert(channel->flag_mutex);

	switch_mutex_lock(channel->flag_mutex);
	if (flag == CF_LEG_HOLDING && !channel->flags[flag] && channel->flags[CF_ANSWERED]) {
		HELD = 1;
	}
	if (channel->flags[flag] != value) {
		just_set = 1;
		channel->flags[flag] = value;
	}
	switch_mutex_unlock(channel->flag_mutex);

	if (flag == CF_VIDEO_READY && just_set) {
		switch_core_session_request_video_refresh(channel->session);
	}

	if (flag == CF_ORIGINATOR && switch_channel_test_flag(channel, CF_ANSWERED) && switch_channel_up_nosig(channel)) {
		switch_channel_set_callstate(channel, CCS_RING_WAIT);
	}

	if (flag == CF_DIALPLAN) {
		if (channel->direction == SWITCH_CALL_DIRECTION_INBOUND) {
			channel->logical_direction = SWITCH_CALL_DIRECTION_OUTBOUND;
			if (channel->device_node) {
				channel->device_node->direction = SWITCH_CALL_DIRECTION_INBOUND;
			}
		} else {
			channel->logical_direction = SWITCH_CALL_DIRECTION_INBOUND;
			if (channel->device_node) {
				channel->device_node->direction = SWITCH_CALL_DIRECTION_OUTBOUND;
			}
		}
	}

	if (HELD) {
		switch_hold_record_t *hr;
		const char *brto = switch_channel_get_partner_uuid(channel);

		switch_channel_set_callstate(channel, CCS_HELD);
		switch_mutex_lock(channel->profile_mutex);
		channel->caller_profile->times->last_hold = switch_time_now();

		hr = switch_core_session_alloc(channel->session, sizeof(*hr));
		hr->on = switch_time_now();
		if (brto) {
			hr->uuid = switch_core_session_strdup(channel->session, brto);
		}

		if (channel->hold_record) {
			hr->next = channel->hold_record;
		}
		channel->hold_record = hr;

		switch_mutex_unlock(channel->profile_mutex);
	}

	if (flag == CF_OUTBOUND) {
		switch_channel_set_variable(channel, "is_outbound", "true");
	}

	if (flag == CF_RECOVERED) {
		switch_channel_set_variable(channel, "recovered", "true");
	}

	if (flag == CF_VIDEO_ECHO || flag == CF_VIDEO_BLANK || flag == CF_VIDEO_DECODED_READ || flag == CF_VIDEO_PASSIVE) {
		switch_core_session_start_video_thread(channel->session);
	}

	if (flag == CF_VIDEO_DECODED_READ && channel->flags[CF_VIDEO]) {
		switch_core_session_request_video_refresh(channel->session);
	}
}

SWITCH_DECLARE(void) switch_channel_set_flag_recursive(switch_channel_t *channel, switch_channel_flag_t flag)
{
	switch_assert(channel);
	switch_assert(channel->flag_mutex);

	switch_mutex_lock(channel->flag_mutex);
	channel->flags[flag]++;
	switch_mutex_unlock(channel->flag_mutex);

	if (flag == CF_OUTBOUND) {
		switch_channel_set_variable(channel, "is_outbound", "true");
	}

	if (flag == CF_RECOVERED) {
		switch_channel_set_variable(channel, "recovered", "true");
	}
}


SWITCH_DECLARE(void) switch_channel_set_private_flag(switch_channel_t *channel, uint32_t flags)
{
	switch_assert(channel != NULL);
	switch_mutex_lock(channel->flag_mutex);
	channel->private_flags |= flags;
	switch_mutex_unlock(channel->flag_mutex);
}

SWITCH_DECLARE(void) switch_channel_clear_private_flag(switch_channel_t *channel, uint32_t flags)
{
	switch_assert(channel != NULL);
	switch_mutex_lock(channel->flag_mutex);
	channel->private_flags &= ~flags;
	switch_mutex_unlock(channel->flag_mutex);
}

SWITCH_DECLARE(int) switch_channel_test_private_flag(switch_channel_t *channel, uint32_t flags)
{
	switch_assert(channel != NULL);
	return (channel->private_flags & flags);
}

SWITCH_DECLARE(void) switch_channel_set_app_flag_key(const char *key, switch_channel_t *channel, uint32_t flags)
{
	uint32_t *flagp = NULL;
	switch_byte_t new = 0;

	switch_assert(channel != NULL);
	switch_mutex_lock(channel->flag_mutex);

	if (!channel->app_flag_hash) {
		switch_core_hash_init(&channel->app_flag_hash);
		new++;
	}

	if (new || !(flagp = switch_core_hash_find(channel->app_flag_hash, key))) {
		flagp = switch_core_session_alloc(channel->session, sizeof(uint32_t));
		switch_core_hash_insert(channel->app_flag_hash, key, flagp);
	}

	switch_assert(flagp);
	*flagp |= flags;

	switch_mutex_unlock(channel->flag_mutex);
}

SWITCH_DECLARE(void) switch_channel_clear_app_flag_key(const char *key, switch_channel_t *channel, uint32_t flags)
{
	uint32_t *flagp = NULL;

	switch_assert(channel != NULL);
	switch_mutex_lock(channel->flag_mutex);
	if (channel->app_flag_hash && (flagp = switch_core_hash_find(channel->app_flag_hash, key))) {
		if (!flags) {
			*flagp = 0;
		} else {
			*flagp &= ~flags;
		}
	}
	switch_mutex_unlock(channel->flag_mutex);
}

SWITCH_DECLARE(int) switch_channel_test_app_flag_key(const char *key, switch_channel_t *channel, uint32_t flags)
{
	int r = 0;
	uint32_t *flagp = NULL;
	switch_assert(channel != NULL);

	switch_mutex_lock(channel->flag_mutex);
	if (channel->app_flag_hash && (flagp = switch_core_hash_find(channel->app_flag_hash, key))) {
		r = (*flagp & flags);
	}
	switch_mutex_unlock(channel->flag_mutex);


	return r;
}

SWITCH_DECLARE(void) switch_channel_set_state_flag(switch_channel_t *channel, switch_channel_flag_t flag)
{
	switch_assert(channel != NULL);

	switch_mutex_lock(channel->flag_mutex);
	channel->state_flags[0] = 1;
	channel->state_flags[flag] = 1;
	switch_mutex_unlock(channel->flag_mutex);
}

SWITCH_DECLARE(void) switch_channel_clear_state_flag(switch_channel_t *channel, switch_channel_flag_t flag)
{
	switch_assert(channel != NULL);

	switch_mutex_lock(channel->flag_mutex);
	channel->state_flags[flag] = 0;
	switch_mutex_unlock(channel->flag_mutex);
}

SWITCH_DECLARE(void) switch_channel_clear_flag(switch_channel_t *channel, switch_channel_flag_t flag)
{
	int ACTIVE = 0;
	int CLEAR = 0;

	switch_assert(channel != NULL);
	switch_assert(channel->flag_mutex);

	switch_mutex_lock(channel->flag_mutex);
	if (flag == CF_LEG_HOLDING && channel->flags[flag] && channel->flags[CF_ANSWERED]) {
		ACTIVE = 1;
	}

	if (flag == CF_VIDEO_PASSIVE && channel->flags[CF_VIDEO]) {
		channel->flags[CF_VIDEO_READY] = 1;

		if (channel->flags[flag]) {
			CLEAR = 1;
		}
	}

	channel->flags[flag] = 0;
	switch_mutex_unlock(channel->flag_mutex);

	if (flag == CF_DIALPLAN) {
		if (channel->direction == SWITCH_CALL_DIRECTION_OUTBOUND) {
			channel->logical_direction = SWITCH_CALL_DIRECTION_OUTBOUND;
			if (channel->device_node) {
				channel->device_node->direction = SWITCH_CALL_DIRECTION_INBOUND;
			}
		}
	}

	if (ACTIVE) {
		switch_channel_set_callstate(channel, CCS_UNHELD);
		switch_mutex_lock(channel->profile_mutex);
		if (channel->caller_profile->times->last_hold) {
			channel->caller_profile->times->hold_accum += (switch_time_now() - channel->caller_profile->times->last_hold);
		}

		if (channel->hold_record) {
			channel->hold_record->off = switch_time_now();
		}

		if (switch_channel_test_flag(channel, CF_PROXY_MODE) && switch_channel_test_flag(channel, CF_BRIDGED)) {
			switch_channel_set_callstate(channel, CCS_ACTIVE);
		}

		switch_mutex_unlock(channel->profile_mutex);
	}

	if (flag == CF_ORIGINATOR && switch_channel_test_flag(channel, CF_ANSWERED) && switch_channel_up_nosig(channel)) {
		switch_channel_set_callstate(channel, CCS_ACTIVE);
	}

	if (flag == CF_OUTBOUND) {
		switch_channel_set_variable(channel, "is_outbound", NULL);
	}

	if (flag == CF_RECOVERED) {
		switch_channel_set_variable(channel, "recovered", NULL);
	}

	if (flag == CF_VIDEO_PASSIVE && CLEAR) {
		switch_core_session_wake_video_thread(channel->session);
	}

	if (flag == CF_RECOVERING && !channel->hangup_cause && !switch_channel_test_flag(channel, CF_NO_RECOVER)) {
		switch_core_recovery_track(channel->session);
	}

}


SWITCH_DECLARE(void) switch_channel_clear_flag_recursive(switch_channel_t *channel, switch_channel_flag_t flag)
{
	switch_assert(channel != NULL);
	switch_assert(channel->flag_mutex);

	switch_mutex_lock(channel->flag_mutex);
	if (channel->flags[flag]) {
		channel->flags[flag]--;
	}
	switch_mutex_unlock(channel->flag_mutex);

	if (flag == CF_OUTBOUND) {
		switch_channel_set_variable(channel, "is_outbound", NULL);
	}
}

SWITCH_DECLARE(switch_channel_state_t) switch_channel_get_state(switch_channel_t *channel)
{
	switch_channel_state_t state;
	switch_assert(channel != NULL);

	state = channel->state;

	return state;
}

SWITCH_DECLARE(switch_channel_state_t) switch_channel_get_running_state(switch_channel_t *channel)
{
	switch_channel_state_t state;
	switch_assert(channel != NULL);

	state = channel->running_state;

	return state;
}

SWITCH_DECLARE(int) switch_channel_state_change_pending(switch_channel_t *channel)
{
	if (switch_channel_down_nosig(channel) || !switch_core_session_in_thread(channel->session)) {
		return 0;
	}

	return channel->running_state != channel->state;
}

SWITCH_DECLARE(int) switch_channel_check_signal(switch_channel_t *channel, switch_bool_t in_thread_only)
{
	switch_ivr_parse_signal_data(channel->session, SWITCH_FALSE, in_thread_only);
	return 0;
}

SWITCH_DECLARE(int) switch_channel_test_ready(switch_channel_t *channel, switch_bool_t check_ready, switch_bool_t check_media)
{
	int ret = 0;

	switch_assert(channel != NULL);

	switch_channel_check_signal(channel, SWITCH_TRUE);

	if (check_media) {
		ret = ((switch_channel_test_flag(channel, CF_ANSWERED) ||
				switch_channel_test_flag(channel, CF_EARLY_MEDIA)) && !switch_channel_test_flag(channel, CF_PROXY_MODE) &&
			   switch_core_session_get_read_codec(channel->session) && switch_core_session_get_write_codec(channel->session));


		if (!ret)
			return ret;
	}

	if (!check_ready)
		return ret;

	ret = 0;

	if (!channel->hangup_cause && channel->state > CS_ROUTING && channel->state < CS_HANGUP && channel->state != CS_RESET &&
		!switch_channel_test_flag(channel, CF_TRANSFER) && !switch_channel_test_flag(channel, CF_NOT_READY) &&
		!switch_channel_state_change_pending(channel)) {
		ret++;
	}



	return ret;
}

static const char *state_names[] = {
	"CS_NEW",
	"CS_INIT",
	"CS_ROUTING",
	"CS_SOFT_EXECUTE",
	"CS_EXECUTE",
	"CS_EXCHANGE_MEDIA",
	"CS_PARK",
	"CS_CONSUME_MEDIA",
	"CS_HIBERNATE",
	"CS_RESET",
	"CS_HANGUP",
	"CS_REPORTING",
	"CS_DESTROY",
	"CS_NONE",
	NULL
};

SWITCH_DECLARE(const char *) switch_channel_state_name(switch_channel_state_t state)
{
	return state_names[state];
}


SWITCH_DECLARE(switch_channel_state_t) switch_channel_name_state(const char *name)
{
	uint32_t x = 0;
	for (x = 0; state_names[x]; x++) {
		if (!strcasecmp(state_names[x], name)) {
			return (switch_channel_state_t) x;
		}
	}

	return CS_DESTROY;
}

static inline void careful_set(switch_channel_t *channel, switch_channel_state_t *state, switch_channel_state_t val) {

	if (switch_mutex_trylock(channel->thread_mutex) == SWITCH_STATUS_SUCCESS) {
		*state = val;
		switch_mutex_unlock(channel->thread_mutex);
	} else {
		switch_mutex_t *mutex = switch_core_session_get_mutex(channel->session);
		int x = 0;

		for (x = 0; x < 100; x++) {
			if (switch_mutex_trylock(mutex) == SWITCH_STATUS_SUCCESS) {
				*state = val;
				switch_mutex_unlock(mutex);
				break;
			} else {
				switch_cond_next();
			}
		}

		if (x == 100) {
			*state = val;
		}

	}
}

SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_set_running_state(switch_channel_t *channel, switch_channel_state_t state,
																				const char *file, const char *func, int line)
{
	int x;

	switch_mutex_lock(channel->flag_mutex);
	if (channel->state_flags[0]) {
		for (x = 1; x < CF_FLAG_MAX; x++) {
			if (channel->state_flags[x]) {
				channel->flags[x] = 1;
				channel->state_flags[x] = 0;
			}
		}
		channel->state_flags[0] = 0;
	}
	switch_mutex_unlock(channel->flag_mutex);

	switch_channel_clear_flag(channel, CF_TAGGED);


	switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_DEBUG, "(%s) Running State Change %s (Cur %d Tot %" SWITCH_SIZE_T_FMT ")\n",
					  channel->name, state_names[state], switch_core_session_count(), switch_core_session_id() - 1);

	switch_mutex_lock(channel->state_mutex);

	careful_set(channel, &channel->running_state, state);

	if (state <= CS_DESTROY) {
		switch_event_t *event;

		if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
			if (state < CS_HANGUP) {
				if (state == CS_ROUTING) {
					switch_channel_set_callstate(channel, CCS_RINGING);
				} else if (switch_channel_test_flag(channel, CF_ANSWERED)) {
					switch_channel_set_callstate(channel, CCS_ACTIVE);
				} else if (switch_channel_test_flag(channel, CF_EARLY_MEDIA)) {
					switch_channel_set_callstate(channel, CCS_EARLY);
				}
			}
		}

		if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_STATE) == SWITCH_STATUS_SUCCESS) {
			switch_channel_event_set_data(channel, event);
			switch_event_fire(&event);
		}
	}

	switch_mutex_unlock(channel->state_mutex);

	return (switch_channel_state_t) SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_set_state(switch_channel_t *channel,
																		const char *file, const char *func, int line, switch_channel_state_t state)
{
	switch_channel_state_t last_state;
	int ok = 0;

	switch_assert(channel != NULL);
	switch_assert(state <= CS_DESTROY);
	switch_mutex_lock(channel->state_mutex);

	last_state = channel->state;
	switch_assert(last_state <= CS_DESTROY);

	if (last_state == state) {
		goto done;
	}

	if (last_state >= CS_HANGUP && state < last_state) {
		goto done;
	}

	/* STUB for more dev
	   case CS_INIT:
	   switch(state) {

	   case CS_NEW:
	   case CS_INIT:
	   case CS_EXCHANGE_MEDIA:
	   case CS_SOFT_EXECUTE:
	   case CS_ROUTING:
	   case CS_EXECUTE:
	   case CS_HANGUP:
	   case CS_DESTROY:

	   default:
	   break;
	   }
	   break;
	 */

	switch (last_state) {
	case CS_NEW:
	case CS_RESET:
		switch (state) {
		default:
			ok++;
			break;
		}
		break;

	case CS_INIT:
		switch (state) {
		case CS_EXCHANGE_MEDIA:
		case CS_SOFT_EXECUTE:
		case CS_ROUTING:
		case CS_EXECUTE:
		case CS_PARK:
		case CS_CONSUME_MEDIA:
		case CS_HIBERNATE:
		case CS_RESET:
			ok++;
		default:
			break;
		}
		break;

	case CS_EXCHANGE_MEDIA:
		switch (state) {
		case CS_SOFT_EXECUTE:
		case CS_ROUTING:
		case CS_EXECUTE:
		case CS_PARK:
		case CS_CONSUME_MEDIA:
		case CS_HIBERNATE:
		case CS_RESET:
			ok++;
		default:
			break;
		}
		break;

	case CS_SOFT_EXECUTE:
		switch (state) {
		case CS_EXCHANGE_MEDIA:
		case CS_ROUTING:
		case CS_EXECUTE:
		case CS_PARK:
		case CS_CONSUME_MEDIA:
		case CS_HIBERNATE:
		case CS_RESET:
			ok++;
		default:
			break;
		}
		break;

	case CS_PARK:
		switch (state) {
		case CS_EXCHANGE_MEDIA:
		case CS_ROUTING:
		case CS_EXECUTE:
		case CS_SOFT_EXECUTE:
		case CS_HIBERNATE:
		case CS_RESET:
		case CS_CONSUME_MEDIA:
			ok++;
		default:
			break;
		}
		break;

	case CS_CONSUME_MEDIA:
		switch (state) {
		case CS_EXCHANGE_MEDIA:
		case CS_ROUTING:
		case CS_EXECUTE:
		case CS_SOFT_EXECUTE:
		case CS_HIBERNATE:
		case CS_RESET:
		case CS_PARK:
			ok++;
		default:
			break;
		}
		break;
	case CS_HIBERNATE:
		switch (state) {
		case CS_EXCHANGE_MEDIA:
		case CS_INIT:
		case CS_ROUTING:
		case CS_EXECUTE:
		case CS_SOFT_EXECUTE:
		case CS_PARK:
		case CS_CONSUME_MEDIA:
		case CS_RESET:
			ok++;
		default:
			break;
		}
		break;

	case CS_ROUTING:
		switch (state) {
		case CS_EXCHANGE_MEDIA:
		case CS_EXECUTE:
		case CS_SOFT_EXECUTE:
		case CS_PARK:
		case CS_CONSUME_MEDIA:
		case CS_HIBERNATE:
		case CS_RESET:
			ok++;
		default:
			break;
		}
		break;

	case CS_EXECUTE:
		switch (state) {
		case CS_EXCHANGE_MEDIA:
		case CS_SOFT_EXECUTE:
		case CS_ROUTING:
		case CS_PARK:
		case CS_CONSUME_MEDIA:
		case CS_HIBERNATE:
		case CS_RESET:
			ok++;
		default:
			break;
		}
		break;

	case CS_HANGUP:
		switch (state) {
		case CS_REPORTING:
		case CS_DESTROY:
			ok++;
		default:
			break;
		}
		break;

	case CS_REPORTING:
		switch (state) {
		case CS_DESTROY:
			ok++;
		default:
			break;
		}
		break;

	default:
		break;

	}

	if (ok) {
		switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_DEBUG, "(%s) State Change %s -> %s\n",
						  channel->name, state_names[last_state], state_names[state]);

		careful_set(channel, &channel->state, state);

		if (state == CS_HANGUP && !channel->hangup_cause) {
			channel->hangup_cause = SWITCH_CAUSE_NORMAL_CLEARING;
		}

		if (state <= CS_DESTROY) {
			switch_core_session_signal_state_change(channel->session);
		}
	} else {
		switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_WARNING,
						  "(%s) Invalid State Change %s -> %s\n", channel->name, state_names[last_state], state_names[state]);
		/* we won't tolerate an invalid state change so we can make sure we are as robust as a nice cup of dark coffee! */
		/* not cool lets crash this bad boy and figure out wtf is going on */
		switch_assert(channel->state >= CS_HANGUP);
	}
  done:

	switch_mutex_unlock(channel->state_mutex);
	return channel->state;
}

SWITCH_DECLARE(void) switch_channel_state_thread_lock(switch_channel_t *channel)
{
	switch_mutex_lock(channel->thread_mutex);
}


SWITCH_DECLARE(switch_status_t) switch_channel_state_thread_trylock(switch_channel_t *channel)
{
	return switch_mutex_trylock(channel->thread_mutex);
}


SWITCH_DECLARE(void) switch_channel_state_thread_unlock(switch_channel_t *channel)
{
	switch_mutex_unlock(channel->thread_mutex);
}

SWITCH_DECLARE(void) switch_channel_event_set_basic_data(switch_channel_t *channel, switch_event_t *event)
{
	switch_caller_profile_t *caller_profile, *originator_caller_profile = NULL, *originatee_caller_profile = NULL;
	switch_codec_implementation_t impl = { 0 };
	char state_num[25];
	const char *v;

	switch_mutex_lock(channel->profile_mutex);

	if ((caller_profile = channel->caller_profile)) {
		originator_caller_profile = caller_profile->originator_caller_profile;
		originatee_caller_profile = caller_profile->originatee_caller_profile;
	}

	switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-State", switch_channel_state_name(channel->running_state));
	switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Call-State", switch_channel_callstate2str(channel->callstate));
	switch_snprintf(state_num, sizeof(state_num), "%d", channel->state);
	switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-State-Number", state_num);
	switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Name", switch_channel_get_name(channel));
	switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(channel->session));
	switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Session-External-ID", switch_core_session_get_external_id(channel->session));

	switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Call-Direction",
								   channel->direction == SWITCH_CALL_DIRECTION_OUTBOUND ? "outbound" : "inbound");
	switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Presence-Call-Direction",
								   channel->direction == SWITCH_CALL_DIRECTION_OUTBOUND ? "outbound" : "inbound");

	switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-HIT-Dialplan",
								   switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND ||
								   switch_channel_test_flag(channel, CF_DIALPLAN) ? "true" : "false");


	if ((v = switch_channel_get_variable_dup(channel, "presence_id", SWITCH_FALSE, -1))) {
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Presence-ID", v);
	}

	if ((v = switch_channel_get_variable_dup(channel, "presence_data", SWITCH_FALSE, -1))) {
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Presence-Data", v);
	}


	if ((v = switch_channel_get_variable_dup(channel, "presence_data_cols", SWITCH_FALSE, -1))) {
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Presence-Data-Cols", v);
		switch_event_add_presence_data_cols(channel, event, "PD-");
	}

	if ((v = switch_channel_get_variable_dup(channel, "call_uuid", SWITCH_FALSE, -1))) {
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Call-UUID", v);
	} else {
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Call-UUID", switch_core_session_get_uuid(channel->session));
	}

	if (switch_channel_down_nosig(channel)) {
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Answer-State", "hangup");
	} else if (switch_channel_test_flag(channel, CF_ANSWERED)) {
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Answer-State", "answered");
	} else if (switch_channel_test_flag(channel, CF_EARLY_MEDIA)) {
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Answer-State", "early");
	} else {
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Answer-State", "ringing");
	}

	if (channel->hangup_cause) {
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Hangup-Cause", switch_channel_cause2str(channel->hangup_cause));
	}


	switch_core_session_get_read_impl(channel->session, &impl);

	if (impl.iananame) {
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Read-Codec-Name", impl.iananame);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Read-Codec-Rate", "%u", impl.actual_samples_per_second);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Read-Codec-Bit-Rate", "%d", impl.bits_per_second);
	}

	switch_core_session_get_write_impl(channel->session, &impl);

	if (impl.iananame) {
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Write-Codec-Name", impl.iananame);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Write-Codec-Rate", "%u", impl.actual_samples_per_second);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Channel-Write-Codec-Bit-Rate", "%d", impl.bits_per_second);
	}

	/* Index Caller's Profile */
	if (caller_profile) {
		switch_caller_profile_event_set_data(caller_profile, "Caller", event);
	}

	/* Index Originator/ee's Profile */
	if (originator_caller_profile && channel->last_profile_type == LP_ORIGINATOR) {
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Other-Type", "originator");
		switch_caller_profile_event_set_data(originator_caller_profile, "Other-Leg", event);
	} else if (originatee_caller_profile && channel->last_profile_type == LP_ORIGINATEE) {	/* Index Originatee's Profile */
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Other-Type", "originatee");
		switch_caller_profile_event_set_data(originatee_caller_profile, "Other-Leg", event);
	}


	switch_mutex_unlock(channel->profile_mutex);
}

SWITCH_DECLARE(void) switch_channel_event_set_extended_data(switch_channel_t *channel, switch_event_t *event)
{
	switch_event_header_t *hi;
	int global_verbose_events = -1;

	switch_mutex_lock(channel->profile_mutex);

	switch_core_session_ctl(SCSC_VERBOSE_EVENTS, &global_verbose_events);

	if (global_verbose_events ||
		switch_channel_test_flag(channel, CF_VERBOSE_EVENTS) ||
		switch_event_get_header(event, "presence-data-cols") ||
		event->event_id == SWITCH_EVENT_CHANNEL_CREATE ||
		event->event_id == SWITCH_EVENT_CHANNEL_ORIGINATE ||
		event->event_id == SWITCH_EVENT_CHANNEL_UUID ||
		event->event_id == SWITCH_EVENT_CHANNEL_ANSWER ||
		event->event_id == SWITCH_EVENT_CHANNEL_PARK ||
		event->event_id == SWITCH_EVENT_CHANNEL_UNPARK ||
		event->event_id == SWITCH_EVENT_CHANNEL_BRIDGE ||
		event->event_id == SWITCH_EVENT_CHANNEL_UNBRIDGE ||
		event->event_id == SWITCH_EVENT_CHANNEL_PROGRESS ||
		event->event_id == SWITCH_EVENT_CHANNEL_PROGRESS_MEDIA ||
		event->event_id == SWITCH_EVENT_CHANNEL_HANGUP ||
		event->event_id == SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE ||
		event->event_id == SWITCH_EVENT_REQUEST_PARAMS ||
		event->event_id == SWITCH_EVENT_CHANNEL_DATA ||
		event->event_id == SWITCH_EVENT_CHANNEL_EXECUTE ||
		event->event_id == SWITCH_EVENT_CHANNEL_EXECUTE_COMPLETE ||
		event->event_id == SWITCH_EVENT_CHANNEL_DESTROY ||
		event->event_id == SWITCH_EVENT_SESSION_HEARTBEAT ||
		event->event_id == SWITCH_EVENT_API ||
		event->event_id == SWITCH_EVENT_RECORD_START ||
		event->event_id == SWITCH_EVENT_RECORD_STOP ||
		event->event_id == SWITCH_EVENT_PLAYBACK_START ||
		event->event_id == SWITCH_EVENT_PLAYBACK_STOP ||
		event->event_id == SWITCH_EVENT_CALL_UPDATE || 
		event->event_id == SWITCH_EVENT_MEDIA_BUG_START || 
		event->event_id == SWITCH_EVENT_MEDIA_BUG_STOP || 
		event->event_id == SWITCH_EVENT_CHANNEL_HOLD || 
		event->event_id == SWITCH_EVENT_CHANNEL_UNHOLD || 
		event->event_id == SWITCH_EVENT_TEXT || 
		event->event_id == SWITCH_EVENT_CUSTOM) {

		/* Index Variables */

		if (channel->scope_variables) {
			switch_event_t *ep;

			for (ep = channel->scope_variables; ep; ep = ep->next) {
				for (hi = ep->headers; hi; hi = hi->next) {
					char buf[1024];
					char *vvar = NULL, *vval = NULL;

					vvar = (char *) hi->name;
					vval = (char *) hi->value;

					switch_assert(vvar && vval);
					switch_snprintf(buf, sizeof(buf), "scope_variable_%s", vvar);

					if (!switch_event_get_header(event, buf)) {
						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, buf, vval);
					}
				}
			}
		}

		if (channel->variables) {
			for (hi = channel->variables->headers; hi; hi = hi->next) {
				char buf[1024];
				char *vvar = NULL, *vval = NULL;

				vvar = (char *) hi->name;
				vval = (char *) hi->value;

				switch_assert(vvar && vval);
				switch_snprintf(buf, sizeof(buf), "variable_%s", vvar);
				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, buf, vval);
			}
		}
	}

	switch_mutex_unlock(channel->profile_mutex);
}


SWITCH_DECLARE(void) switch_channel_event_set_data(switch_channel_t *channel, switch_event_t *event)
{
	switch_mutex_lock(channel->profile_mutex);
	switch_channel_event_set_basic_data(channel, event);
	switch_channel_event_set_extended_data(channel, event);
	switch_mutex_unlock(channel->profile_mutex);
}

SWITCH_DECLARE(void) switch_channel_step_caller_profile(switch_channel_t *channel)
{
	switch_caller_profile_t *cp;


	switch_mutex_lock(channel->profile_mutex);
	cp = switch_caller_profile_clone(channel->session, channel->caller_profile);
	switch_mutex_unlock(channel->profile_mutex);

	switch_channel_set_caller_profile(channel, cp);
}

SWITCH_DECLARE(void) switch_channel_set_caller_profile(switch_channel_t *channel, switch_caller_profile_t *caller_profile)
{
	char *uuid = NULL;
	switch_assert(channel != NULL);
	switch_assert(channel->session != NULL);
	switch_mutex_lock(channel->profile_mutex);
	switch_assert(caller_profile != NULL);

	caller_profile->direction = channel->direction;
	caller_profile->logical_direction = channel->logical_direction;
	uuid = switch_core_session_get_uuid(channel->session);

	if (!caller_profile->uuid || strcasecmp(caller_profile->uuid, uuid)) {
		caller_profile->uuid = switch_core_session_strdup(channel->session, uuid);
	}

	if (!caller_profile->chan_name || strcasecmp(caller_profile->chan_name, channel->name)) {
		caller_profile->chan_name = switch_core_session_strdup(channel->session, channel->name);
	}

	if (!caller_profile->context) {
		caller_profile->context = switch_core_session_strdup(channel->session, "default");
	}

	if (!caller_profile->times) {
		caller_profile->times = (switch_channel_timetable_t *) switch_core_session_alloc(channel->session, sizeof(*caller_profile->times));
		caller_profile->times->profile_created = switch_micro_time_now();
	}

	if (channel->caller_profile && channel->caller_profile->times) {
		channel->caller_profile->times->transferred = caller_profile->times->profile_created;
		caller_profile->times->answered = channel->caller_profile->times->answered;
		caller_profile->times->progress = channel->caller_profile->times->progress;
		caller_profile->times->progress_media = channel->caller_profile->times->progress_media;
		caller_profile->times->created = channel->caller_profile->times->created;
		caller_profile->times->hungup = channel->caller_profile->times->hungup;
		if (channel->caller_profile->caller_extension) {
			switch_caller_extension_clone(&caller_profile->caller_extension, channel->caller_profile->caller_extension, caller_profile->pool);
		}
	} else {
		caller_profile->times->created = switch_micro_time_now();
	}


	caller_profile->next = channel->caller_profile;
	channel->caller_profile = caller_profile;
	caller_profile->profile_index = switch_core_sprintf(caller_profile->pool, "%d", ++channel->profile_index);

	switch_mutex_unlock(channel->profile_mutex);
}

SWITCH_DECLARE(switch_caller_profile_t *) switch_channel_get_caller_profile(switch_channel_t *channel)
{
	switch_caller_profile_t *profile;
	switch_assert(channel != NULL);
	switch_mutex_lock(channel->profile_mutex);
	if ((profile = channel->caller_profile) && profile->hunt_caller_profile) {
		profile = profile->hunt_caller_profile;
	}
	switch_mutex_unlock(channel->profile_mutex);
	return profile;
}

SWITCH_DECLARE(void) switch_channel_set_originator_caller_profile(switch_channel_t *channel, switch_caller_profile_t *caller_profile)
{
	switch_assert(channel != NULL);
	switch_assert(channel->caller_profile != NULL);
	switch_mutex_lock(channel->profile_mutex);

	if (!caller_profile->times) {
		caller_profile->times = (switch_channel_timetable_t *) switch_core_alloc(caller_profile->pool, sizeof(*caller_profile->times));
	}

	if (channel->caller_profile) {
		caller_profile->next = channel->caller_profile->originator_caller_profile;
		channel->caller_profile->originator_caller_profile = caller_profile;
		channel->last_profile_type = LP_ORIGINATOR;
	}
	switch_assert(channel->caller_profile->originator_caller_profile->next != channel->caller_profile->originator_caller_profile);
	switch_mutex_unlock(channel->profile_mutex);
}

SWITCH_DECLARE(void) switch_channel_set_hunt_caller_profile(switch_channel_t *channel, switch_caller_profile_t *caller_profile)
{
	switch_assert(channel != NULL);
	switch_assert(channel->caller_profile != NULL);

	switch_mutex_lock(channel->profile_mutex);

	channel->caller_profile->hunt_caller_profile = NULL;
	if (channel->caller_profile && caller_profile) {
		caller_profile->direction = channel->direction;
		caller_profile->logical_direction = channel->logical_direction;
		channel->caller_profile->hunt_caller_profile = caller_profile;
	}
	switch_mutex_unlock(channel->profile_mutex);
}

SWITCH_DECLARE(void) switch_channel_set_origination_caller_profile(switch_channel_t *channel, switch_caller_profile_t *caller_profile)
{
	switch_assert(channel != NULL);
	switch_assert(channel->caller_profile != NULL);

	switch_mutex_lock(channel->profile_mutex);

	if (channel->caller_profile) {
		caller_profile->next = channel->caller_profile->origination_caller_profile;
		channel->caller_profile->origination_caller_profile = caller_profile;
	}
	switch_assert(channel->caller_profile->origination_caller_profile->next != channel->caller_profile->origination_caller_profile);
	switch_mutex_unlock(channel->profile_mutex);
}

SWITCH_DECLARE(switch_caller_profile_t *) switch_channel_get_origination_caller_profile(switch_channel_t *channel)
{
	switch_caller_profile_t *profile = NULL;
	switch_assert(channel != NULL);

	switch_mutex_lock(channel->profile_mutex);
	if (channel->caller_profile) {
		profile = channel->caller_profile->origination_caller_profile;
	}
	switch_mutex_unlock(channel->profile_mutex);

	return profile;
}


SWITCH_DECLARE(void) switch_channel_set_originatee_caller_profile(switch_channel_t *channel, switch_caller_profile_t *caller_profile)
{
	switch_assert(channel != NULL);
	switch_assert(channel->caller_profile != NULL);

	switch_mutex_lock(channel->profile_mutex);

	if (channel->caller_profile) {
		caller_profile->next = channel->caller_profile->originatee_caller_profile;
		channel->caller_profile->originatee_caller_profile = caller_profile;
		channel->last_profile_type = LP_ORIGINATEE;
	}
	switch_assert(channel->caller_profile->originatee_caller_profile->next != channel->caller_profile->originatee_caller_profile);
	switch_mutex_unlock(channel->profile_mutex);
}

SWITCH_DECLARE(switch_caller_profile_t *) switch_channel_get_originator_caller_profile(switch_channel_t *channel)
{
	switch_caller_profile_t *profile = NULL;
	switch_assert(channel != NULL);

	switch_mutex_lock(channel->profile_mutex);

	if (channel->caller_profile) {
		profile = channel->caller_profile->originator_caller_profile;
	}
	switch_mutex_unlock(channel->profile_mutex);

	return profile;
}

SWITCH_DECLARE(switch_caller_profile_t *) switch_channel_get_originatee_caller_profile(switch_channel_t *channel)
{
	switch_caller_profile_t *profile = NULL;
	switch_assert(channel != NULL);

	switch_mutex_lock(channel->profile_mutex);
	if (channel->caller_profile) {
		profile = channel->caller_profile->originatee_caller_profile;
	}
	switch_mutex_unlock(channel->profile_mutex);

	return profile;
}

SWITCH_DECLARE(char *) switch_channel_get_uuid(switch_channel_t *channel)
{
	switch_assert(channel != NULL);
	switch_assert(channel->session != NULL);
	return switch_core_session_get_uuid(channel->session);
}

SWITCH_DECLARE(int) switch_channel_add_state_handler(switch_channel_t *channel, const switch_state_handler_table_t *state_handler)
{
	int x, index;

	switch_assert(channel != NULL);
	switch_mutex_lock(channel->state_mutex);
	for (x = 0; x < SWITCH_MAX_STATE_HANDLERS; x++) {
		if (channel->state_handlers[x] == state_handler) {
			index = x;
			goto end;
		}
	}
	index = channel->state_handler_index++;

	if (channel->state_handler_index >= SWITCH_MAX_STATE_HANDLERS) {
		index = -1;
		goto end;
	}

	channel->state_handlers[index] = state_handler;

  end:
	switch_mutex_unlock(channel->state_mutex);
	return index;
}

SWITCH_DECLARE(const switch_state_handler_table_t *) switch_channel_get_state_handler(switch_channel_t *channel, int index)
{
	const switch_state_handler_table_t *h = NULL;

	switch_assert(channel != NULL);

	if (index >= SWITCH_MAX_STATE_HANDLERS || index > channel->state_handler_index) {
		return NULL;
	}

	switch_mutex_lock(channel->state_mutex);
	h = channel->state_handlers[index];
	switch_mutex_unlock(channel->state_mutex);

	return h;
}

SWITCH_DECLARE(void) switch_channel_clear_state_handler(switch_channel_t *channel, const switch_state_handler_table_t *state_handler)
{
	int index, i = channel->state_handler_index;
	const switch_state_handler_table_t *new_handlers[SWITCH_MAX_STATE_HANDLERS] = { 0 };

	switch_assert(channel != NULL);

	switch_mutex_lock(channel->state_mutex);
	channel->state_handler_index = 0;

	if (state_handler) {
		for (index = 0; index < i; index++) {
			if (channel->state_handlers[index] != state_handler) {
				new_handlers[channel->state_handler_index++] = channel->state_handlers[index];
			}
		}
	} else {
		for (index = 0; index < i; index++) {
			if (channel->state_handlers[index] && switch_test_flag(channel->state_handlers[index], SSH_FLAG_STICKY)) {
				new_handlers[channel->state_handler_index++] = channel->state_handlers[index];
			}
		}
	}

	for (index = 0; index < SWITCH_MAX_STATE_HANDLERS; index++) {
		channel->state_handlers[index] = NULL;
	}

	if (channel->state_handler_index > 0) {
		for (index = 0; index < channel->state_handler_index; index++) {
			channel->state_handlers[index] = new_handlers[index];
		}
	}

	switch_mutex_unlock(channel->state_mutex);
}

SWITCH_DECLARE(void) switch_channel_restart(switch_channel_t *channel)
{
	switch_channel_set_state(channel, CS_RESET);
	switch_channel_wait_for_state_timeout(channel, CS_RESET, 5000);
	switch_channel_set_state(channel, CS_EXECUTE);
}

/* XXX This is a somewhat simple operation.  Were essentially taking the extension that one channel
   was executing and generating a new extension for another channel that starts out where the
   original one left off with an optional forward offset.  Since all we are really doing is
   copying a few basic pool-allocated structures from one channel to another there really is
   not much to worry about here in terms of threading since we use read-write locks.
   While the features are nice, they only really are needed in one specific crazy attended
   transfer scenario where one channel was in the middle of calling a particular extension
   when it was rudely cut off by a transfer key press. XXX */

SWITCH_DECLARE(switch_status_t) switch_channel_caller_extension_masquerade(switch_channel_t *orig_channel, switch_channel_t *new_channel, uint32_t offset)
{
	switch_caller_profile_t *caller_profile;
	switch_caller_extension_t *extension = NULL, *orig_extension = NULL;
	switch_caller_application_t *ap;
	switch_status_t status = SWITCH_STATUS_FALSE;
	switch_event_header_t *hi = NULL;
	const char *no_copy = switch_channel_get_variable(orig_channel, "attended_transfer_no_copy");
	char *dup;
	int i, argc = 0;
	char *argv[128];

	if (no_copy) {
		dup = switch_core_session_strdup(new_channel->session, no_copy);
		argc = switch_separate_string(dup, ',', argv, (sizeof(argv) / sizeof(argv[0])));
	}


	switch_mutex_lock(orig_channel->profile_mutex);
	switch_mutex_lock(new_channel->profile_mutex);


	caller_profile = switch_caller_profile_clone(new_channel->session, new_channel->caller_profile);
	switch_assert(caller_profile);
	extension = switch_caller_extension_new(new_channel->session, caller_profile->destination_number, caller_profile->destination_number);
	orig_extension = switch_channel_get_caller_extension(orig_channel);


	if (extension && orig_extension) {
		for (ap = orig_extension->current_application; ap && offset > 0; offset--) {
			ap = ap->next;
		}

		for (; ap; ap = ap->next) {
			switch_caller_extension_add_application(new_channel->session, extension, ap->application_name, ap->application_data);
		}

		caller_profile->destination_number = switch_core_strdup(caller_profile->pool, orig_channel->caller_profile->destination_number);
		switch_channel_set_caller_profile(new_channel, caller_profile);
		switch_channel_set_caller_extension(new_channel, extension);

		for (hi = orig_channel->variables->headers; hi; hi = hi->next) {
			int ok = 1;
			for (i = 0; i < argc; i++) {
				if (!strcasecmp(argv[i], hi->name)) {
					ok = 0;
					break;
				}
			}

			if (!ok)
				continue;

			switch_channel_set_variable(new_channel, hi->name, hi->value);
		}

		status = SWITCH_STATUS_SUCCESS;
	}


	switch_mutex_unlock(new_channel->profile_mutex);
	switch_mutex_unlock(orig_channel->profile_mutex);


	return status;
}

SWITCH_DECLARE(void) switch_channel_invert_cid(switch_channel_t *channel)
{
	const char *tname, *tnum;
	switch_caller_profile_t *cp;

	cp = switch_channel_get_caller_profile(channel);

	tname = cp->caller_id_name;
	tnum = cp->caller_id_number;

#ifdef DEEP_DEBUG_CID
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "SWAP [%s][%s] [%s][%s]\n", cp->caller_id_name, cp->caller_id_number, cp->callee_id_name, cp->callee_id_number);
#endif

	cp->caller_id_name = cp->callee_id_name;
	cp->caller_id_number = cp->callee_id_number;

	cp->callee_id_name = tname;
	cp->callee_id_number = tnum;

	if (zstr(cp->caller_id_name)) {
		cp->caller_id_name = "Unknown";
	}

	if (zstr(cp->caller_id_number)) {
		cp->caller_id_number = "Unknown";
	}
}


SWITCH_DECLARE(void) switch_channel_flip_cid(switch_channel_t *channel)
{
	switch_event_t *event;
	const char *tmp = NULL;

	switch_mutex_lock(channel->profile_mutex);

	if (switch_channel_test_flag(channel, CF_RECOVERING) && switch_true(switch_channel_get_variable(channel, "channel_cid_flipped"))) {
		switch_mutex_unlock(channel->profile_mutex);
		return;
	}

	if (channel->caller_profile->callee_id_name) {
		tmp = channel->caller_profile->caller_id_name;
		switch_channel_set_variable(channel, "pre_transfer_caller_id_name", channel->caller_profile->caller_id_name);
		channel->caller_profile->caller_id_name = switch_core_strdup(channel->caller_profile->pool, channel->caller_profile->callee_id_name);
	}

	if (switch_channel_test_flag(channel, CF_BRIDGED)) {
		channel->caller_profile->callee_id_name = SWITCH_BLANK_STRING;
	} else if (tmp) {
		channel->caller_profile->callee_id_name = tmp;
	}

	if (channel->caller_profile->callee_id_number) {
		tmp = channel->caller_profile->caller_id_number;
		switch_channel_set_variable(channel, "pre_transfer_caller_id_number", channel->caller_profile->caller_id_number);
		channel->caller_profile->caller_id_number = switch_core_strdup(channel->caller_profile->pool, channel->caller_profile->callee_id_number);
	}

	if (switch_channel_test_flag(channel, CF_BRIDGED)) {
		channel->caller_profile->callee_id_number = SWITCH_BLANK_STRING;
	} else if (tmp) {
		channel->caller_profile->callee_id_number = tmp;
	}

	switch_channel_set_variable(channel, "channel_cid_flipped", "yes");

	switch_mutex_unlock(channel->profile_mutex);


	if (switch_event_create(&event, SWITCH_EVENT_CALL_UPDATE) == SWITCH_STATUS_SUCCESS) {
		const char *uuid = switch_channel_get_partner_uuid(channel);
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Direction", "RECV");

		if (uuid) {
			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridged-To", uuid);
		}
		switch_channel_event_set_data(channel, event);
		switch_event_fire(&event);
	}


	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(channel->session), SWITCH_LOG_INFO, "%s Flipping CID from \"%s\" <%s> to \"%s\" <%s>\n",
					  switch_channel_get_name(channel),
					  switch_str_nil(switch_channel_get_variable(channel, "pre_transfer_caller_id_name")),
					  switch_str_nil(switch_channel_get_variable(channel, "pre_transfer_caller_id_number")),
					  channel->caller_profile->caller_id_name,
					  channel->caller_profile->caller_id_number
					  );

}

SWITCH_DECLARE(void) switch_channel_sort_cid(switch_channel_t *channel)
{

	if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND && switch_channel_test_flag(channel, CF_BLEG)) {
		switch_channel_flip_cid(channel);
		switch_channel_clear_flag(channel, CF_BLEG);
	} else if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND && !switch_channel_test_flag(channel, CF_DIALPLAN)) {
		switch_channel_set_flag(channel, CF_DIALPLAN);
		switch_channel_flip_cid(channel);
	}
}

SWITCH_DECLARE(switch_caller_extension_t *) switch_channel_get_queued_extension(switch_channel_t *channel)
{
	switch_caller_extension_t *caller_extension;

	switch_mutex_lock(channel->profile_mutex);
	caller_extension = channel->queued_extension;
	channel->queued_extension = NULL;
	switch_mutex_unlock(channel->profile_mutex);

	return caller_extension;
}

SWITCH_DECLARE(void) switch_channel_transfer_to_extension(switch_channel_t *channel, switch_caller_extension_t *caller_extension)
{
	switch_mutex_lock(channel->profile_mutex);
	channel->queued_extension = caller_extension;
	switch_mutex_unlock(channel->profile_mutex);

	switch_channel_set_flag(channel, CF_TRANSFER);
	switch_channel_set_state(channel, CS_ROUTING);
}

SWITCH_DECLARE(void) switch_channel_set_caller_extension(switch_channel_t *channel, switch_caller_extension_t *caller_extension)
{
	switch_assert(channel != NULL);

	switch_channel_sort_cid(channel);

	switch_mutex_lock(channel->profile_mutex);
	caller_extension->next = channel->caller_profile->caller_extension;
	channel->caller_profile->caller_extension = caller_extension;
	switch_mutex_unlock(channel->profile_mutex);
}


SWITCH_DECLARE(switch_caller_extension_t *) switch_channel_get_caller_extension(switch_channel_t *channel)
{
	switch_caller_extension_t *extension = NULL;

	switch_assert(channel != NULL);
	switch_mutex_lock(channel->profile_mutex);
	if (channel->caller_profile) {
		extension = channel->caller_profile->caller_extension;
	}
	switch_mutex_unlock(channel->profile_mutex);
	return extension;
}


SWITCH_DECLARE(void) switch_channel_set_bridge_time(switch_channel_t *channel)
{
	switch_mutex_lock(channel->profile_mutex);
	if (channel->caller_profile && channel->caller_profile->times) {
		channel->caller_profile->times->bridged = switch_micro_time_now();
	}
	switch_mutex_unlock(channel->profile_mutex);
}


SWITCH_DECLARE(void) switch_channel_set_hangup_time(switch_channel_t *channel)
{
	if (channel->caller_profile && channel->caller_profile->times && !channel->caller_profile->times->hungup) {
		switch_mutex_lock(channel->profile_mutex);
		channel->caller_profile->times->hungup = switch_micro_time_now();
		switch_mutex_unlock(channel->profile_mutex);
	}
}


SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_hangup(switch_channel_t *channel,
																	 const char *file, const char *func, int line, switch_call_cause_t hangup_cause)
{
	int ok = 0;

	switch_assert(channel != NULL);

	/* one per customer */
	switch_mutex_lock(channel->state_mutex);
	if (!(channel->opaque_flags & OCF_HANGUP)) {
		channel->opaque_flags |= OCF_HANGUP;
		ok = 1;
	}
	switch_mutex_unlock(channel->state_mutex);

	if (switch_channel_test_flag(channel, CF_LEG_HOLDING)) {
		switch_channel_mark_hold(channel, SWITCH_FALSE);
		switch_channel_set_flag(channel, CF_HANGUP_HELD);
	}

	if (!ok) {
		return channel->state;
	}

	switch_channel_clear_flag(channel, CF_BLOCK_STATE);

	if (channel->state < CS_HANGUP) {
		switch_channel_state_t last_state;
		switch_event_t *event;
		const char *var;


		switch_mutex_lock(channel->profile_mutex);
		if (channel->hold_record && !channel->hold_record->off) {
			channel->hold_record->off = switch_time_now();
		}
		switch_mutex_unlock(channel->profile_mutex);

		switch_mutex_lock(channel->state_mutex);
		last_state = channel->state;
		channel->state = CS_HANGUP;
		switch_mutex_unlock(channel->state_mutex);

		channel->hangup_cause = hangup_cause;
		switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_NOTICE, "Hangup %s [%s] [%s]\n",
						  channel->name, state_names[last_state], switch_channel_cause2str(channel->hangup_cause));


		switch_channel_set_variable_partner(channel, "last_bridge_hangup_cause", switch_channel_cause2str(hangup_cause));

		if ((var = switch_channel_get_variable(channel, SWITCH_PROTO_SPECIFIC_HANGUP_CAUSE_VARIABLE))) {
			switch_channel_set_variable_partner(channel, "last_bridge_" SWITCH_PROTO_SPECIFIC_HANGUP_CAUSE_VARIABLE, var);
		}

		if (switch_channel_test_flag(channel, CF_BRIDGE_ORIGINATOR)) {
			switch_channel_set_variable(channel, "last_bridge_role", "originator");
		} else if (switch_channel_test_flag(channel, CF_BRIDGED)) {
			switch_channel_set_variable(channel, "last_bridge_role", "originatee");
		}


		if (!switch_core_session_running(channel->session) && !switch_core_session_started(channel->session)) {
			switch_core_session_thread_launch(channel->session);
		}

		if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_HANGUP) == SWITCH_STATUS_SUCCESS) {
			switch_channel_event_set_data(channel, event);
			switch_event_fire(&event);
		}

		switch_core_session_kill_channel(channel->session, SWITCH_SIG_KILL);
		switch_core_session_signal_state_change(channel->session);
		switch_core_session_hangup_state(channel->session, SWITCH_FALSE);
	}

	return channel->state;
}

static switch_status_t send_ind(switch_channel_t *channel, switch_core_session_message_types_t msg_id, const char *file, const char *func, int line)
{
	switch_core_session_message_t msg = { 0 };

	msg.message_id = msg_id;
	msg.from = channel->name;
	return switch_core_session_perform_receive_message(channel->session, &msg, file, func, line);
}

SWITCH_DECLARE(switch_status_t) switch_channel_perform_acknowledge_call(switch_channel_t *channel,
																		const char *file, const char *func, int line)
{
	send_ind(channel, SWITCH_MESSAGE_INDICATE_ACKNOWLEDGE_CALL, file, func, line);

	return SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE(switch_status_t) switch_channel_perform_mark_ring_ready_value(switch_channel_t *channel,
																			 switch_ring_ready_t rv,
																			 const char *file, const char *func, int line)
{
	switch_event_t *event;

	if (!switch_channel_test_flag(channel, CF_RING_READY) && !switch_channel_test_flag(channel, CF_ANSWERED)) {
		switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_NOTICE, "Ring-Ready %s!\n", channel->name);
		switch_channel_set_flag_value(channel, CF_RING_READY, rv);

		switch_mutex_lock(channel->profile_mutex);
		if (channel->caller_profile && channel->caller_profile->times && !channel->caller_profile->times->progress) {
			channel->caller_profile->times->progress = switch_micro_time_now();
			if (channel->caller_profile->originator_caller_profile) {
				switch_core_session_t *other_session;
				if ((other_session = switch_core_session_locate(channel->caller_profile->originator_caller_profile->uuid))) {
					switch_channel_t *other_channel;
					other_channel = switch_core_session_get_channel(other_session);
					switch_mutex_lock(other_channel->profile_mutex);
					if (other_channel->caller_profile && !other_channel->caller_profile->times->progress) {
						other_channel->caller_profile->times->progress = channel->caller_profile->times->progress;
					}
					switch_mutex_unlock(other_channel->profile_mutex);
					switch_core_session_rwunlock(other_session);
				}
				channel->caller_profile->originator_caller_profile->times->progress = channel->caller_profile->times->progress;
			}
		}
		switch_mutex_unlock(channel->profile_mutex);

		if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_PROGRESS) == SWITCH_STATUS_SUCCESS) {
			switch_channel_event_set_data(channel, event);
			switch_event_fire(&event);
		}

		switch_channel_execute_on(channel, SWITCH_CHANNEL_EXECUTE_ON_RING_VARIABLE);
		switch_channel_api_on(channel, SWITCH_CHANNEL_API_ON_RING_VARIABLE);

		switch_channel_set_callstate(channel, CCS_RINGING);

		send_ind(channel, SWITCH_MESSAGE_RING_EVENT, file, func, line);

		return SWITCH_STATUS_SUCCESS;
	}

	return SWITCH_STATUS_FALSE;
}

SWITCH_DECLARE(switch_status_t) switch_channel_perform_mark_pre_answered(switch_channel_t *channel, const char *file, const char *func, int line)
{
	switch_event_t *event;

	if (!switch_channel_test_flag(channel, CF_EARLY_MEDIA) && !switch_channel_test_flag(channel, CF_ANSWERED)) {
		const char *uuid;
		switch_core_session_t *other_session;

		switch_core_media_check_dtls(channel->session, SWITCH_MEDIA_TYPE_AUDIO);

		switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_NOTICE, "Pre-Answer %s!\n", channel->name);
		switch_channel_set_flag(channel, CF_EARLY_MEDIA);

		switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "EARLY MEDIA");

		if (switch_true(switch_channel_get_variable(channel, "video_mirror_input"))) {
			switch_channel_set_flag(channel, CF_VIDEO_MIRROR_INPUT);
		}

		if (channel->caller_profile && channel->caller_profile->times) {
			switch_mutex_lock(channel->profile_mutex);
			channel->caller_profile->times->progress_media = switch_micro_time_now();
			if (channel->caller_profile->originator_caller_profile) {
				switch_core_session_t *osession;
				if ((osession = switch_core_session_locate(channel->caller_profile->originator_caller_profile->uuid))) {
					switch_channel_t *other_channel;
					other_channel = switch_core_session_get_channel(osession);
					if (other_channel->caller_profile) {
						other_channel->caller_profile->times->progress_media = channel->caller_profile->times->progress_media;
					}
					switch_core_session_rwunlock(osession);
				}
				channel->caller_profile->originator_caller_profile->times->progress_media = channel->caller_profile->times->progress_media;
			}
			switch_mutex_unlock(channel->profile_mutex);
		}

		if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_PROGRESS_MEDIA) == SWITCH_STATUS_SUCCESS) {
			switch_channel_event_set_data(channel, event);
			switch_event_fire(&event);
		}

		switch_channel_execute_on(channel, SWITCH_CHANNEL_EXECUTE_ON_PRE_ANSWER_VARIABLE);
		switch_channel_execute_on(channel, SWITCH_CHANNEL_EXECUTE_ON_MEDIA_VARIABLE);

		switch_channel_api_on(channel, SWITCH_CHANNEL_API_ON_PRE_ANSWER_VARIABLE);
		switch_channel_api_on(channel, SWITCH_CHANNEL_API_ON_MEDIA_VARIABLE);

		if (switch_true(switch_channel_get_variable(channel, SWITCH_PASSTHRU_PTIME_MISMATCH_VARIABLE))) {
			switch_channel_set_flag(channel, CF_PASSTHRU_PTIME_MISMATCH);
		}


		/* if we're the child of another channel and the other channel is in a blocking read they will never realize we have answered so send
		   a SWITCH_SIG_BREAK to interrupt any blocking reads on that channel
		 */
		if ((uuid = switch_channel_get_variable(channel, SWITCH_ORIGINATOR_VARIABLE))
			&& (other_session = switch_core_session_locate(uuid))) {
			switch_core_session_kill_channel(other_session, SWITCH_SIG_BREAK);
			switch_core_session_rwunlock(other_session);
		}

		switch_channel_set_callstate(channel, CCS_EARLY);

		send_ind(channel, SWITCH_MESSAGE_PROGRESS_EVENT, file, func, line);

		switch_core_media_check_autoadj(channel->session);

		return SWITCH_STATUS_SUCCESS;
	}

	return SWITCH_STATUS_FALSE;
}

SWITCH_DECLARE(switch_status_t) switch_channel_perform_pre_answer(switch_channel_t *channel, const char *file, const char *func, int line)
{
	switch_core_session_message_t msg = { 0 };
	switch_status_t status = SWITCH_STATUS_SUCCESS;

	switch_assert(channel != NULL);

	if (channel->hangup_cause || channel->state >= CS_HANGUP) {
		return SWITCH_STATUS_FALSE;
	}

	if (switch_channel_test_flag(channel, CF_ANSWERED)) {
		return SWITCH_STATUS_SUCCESS;
	}

	if (switch_channel_test_flag(channel, CF_EARLY_MEDIA)) {
		return SWITCH_STATUS_SUCCESS;
	}

	if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
		msg.message_id = SWITCH_MESSAGE_INDICATE_PROGRESS;
		msg.from = channel->name;
		status = switch_core_session_perform_receive_message(channel->session, &msg, file, func, line);
	}

	if (status == SWITCH_STATUS_SUCCESS) {
		switch_channel_perform_mark_pre_answered(channel, file, func, line);
		switch_channel_audio_sync(channel);
	} else {
		switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
	}

	return status;
}

SWITCH_DECLARE(switch_status_t) switch_channel_perform_ring_ready_value(switch_channel_t *channel, switch_ring_ready_t rv,
																		const char *file, const char *func, int line)
{
	switch_core_session_message_t msg = { 0 };
	switch_status_t status = SWITCH_STATUS_SUCCESS;

	switch_assert(channel != NULL);

	if (channel->hangup_cause || channel->state >= CS_HANGUP) {
		return SWITCH_STATUS_FALSE;
	}

	if (switch_channel_test_flag(channel, CF_ANSWERED)) {
		return SWITCH_STATUS_SUCCESS;
	}

	if (switch_channel_test_flag(channel, CF_EARLY_MEDIA)) {
		return SWITCH_STATUS_SUCCESS;
	}

	if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
		msg.message_id = SWITCH_MESSAGE_INDICATE_RINGING;
		msg.from = channel->name;
		msg.numeric_arg = rv;
		status = switch_core_session_perform_receive_message(channel->session, &msg, file, func, line);
	}

	if (status == SWITCH_STATUS_SUCCESS) {
		switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_NOTICE, "Ring Ready %s!\n", channel->name);
		switch_channel_perform_mark_ring_ready_value(channel, rv, file, func, line);
	} else {
		switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
	}

	return status;
}

static void do_api_on(switch_channel_t *channel, const char *variable)
{
	char *app;
	char *arg = NULL;
	char *expanded = NULL;
	switch_stream_handle_t stream = { 0 };

	app = switch_core_session_strdup(channel->session, variable);

	if ((arg = strchr(app, ' '))) {
		*arg++ = '\0';
	}

	if (zstr(arg)) {
		expanded = arg;
	} else {
		expanded = switch_channel_expand_variables(channel, arg);
	}
	
	SWITCH_STANDARD_STREAM(stream);
	switch_api_execute(app, expanded, NULL, &stream);
	switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "%s process %s: %s(%s)\n%s\n",
					  channel->name, variable, app, switch_str_nil(expanded), (char *) stream.data);

	if (expanded && expanded != arg) {
		free(expanded);
	}

	free(stream.data);
}


SWITCH_DECLARE(switch_status_t) switch_channel_api_on(switch_channel_t *channel, const char *variable_prefix)
{
	switch_event_header_t *hp;
	switch_event_t *event;
	int x = 0;


	switch_channel_get_variables(channel, &event);

	for (hp = event->headers; hp; hp = hp->next) {
		char *var = hp->name;
		char *val = hp->value;

		if (!strncasecmp(var, variable_prefix, strlen(variable_prefix))) {
			if (hp->idx) {
				int i;
				for (i = 0; i < hp->idx; i++) {
					x++;
					do_api_on(channel, hp->array[i]);
				}
			} else {
				x++;
				do_api_on(channel, val);
			}
		}
	}

	switch_event_destroy(&event);

	return x ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
}

SWITCH_DECLARE(switch_status_t) switch_channel_execute_on_value(switch_channel_t *channel, const char *variable_value)
{
	switch_status_t status;
	char *arg = NULL;
	char *p;
	int bg = 0;
	char *app;
	char *expanded = NULL;
	
	app = switch_core_session_strdup(channel->session, variable_value);

	for(p = app; p && *p; p++) {
		if (*p == ' ' || (*p == ':' && (*(p+1) != ':'))) {
			*p++ = '\0';
			arg = p;
			break;
		} else if (*p == ':' && (*(p+1) == ':')) {
			bg++;
			break;
		}
	}

	switch_assert(app != NULL);
	if (!strncasecmp(app, "perl", 4)) {
		bg++;
	}

	if (zstr(arg)) {
		expanded = arg;
	} else {
		expanded = switch_channel_expand_variables(channel, arg);
	}
	
	if (bg) {
		status = switch_core_session_execute_application_async(channel->session, app, arg);
	} else {
		status = switch_core_session_execute_application(channel->session, app, arg);
	}

	if (expanded && expanded != arg) {
		free(expanded);
	}

	return status;
}

SWITCH_DECLARE(switch_status_t) switch_channel_execute_on(switch_channel_t *channel, const char *variable_prefix)
{
	switch_event_header_t *hp;
	switch_event_t *event, *cevent;
	int x = 0;

	switch_core_get_variables(&event);
	switch_channel_get_variables(channel, &cevent);
	switch_event_merge(event, cevent);

	for (hp = event->headers; hp; hp = hp->next) {
		char *var = hp->name;
		char *val = hp->value;

		if (!strncasecmp(var, variable_prefix, strlen(variable_prefix))) {
			if (hp->idx) {
				int i;
				for (i = 0; i < hp->idx; i++) {
					x++;
					switch_channel_execute_on_value(channel, hp->array[i]);
				}
			} else {
				x++;
				switch_channel_execute_on_value(channel, val);
			}
		}
	}

	switch_event_destroy(&event);
	switch_event_destroy(&cevent);

	return x ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
}

SWITCH_DECLARE(switch_status_t) switch_channel_perform_mark_answered(switch_channel_t *channel, const char *file, const char *func, int line)
{
	switch_event_t *event;
	const char *uuid;
	switch_core_session_t *other_session;
	const char *var;

	switch_assert(channel != NULL);

	if (channel->hangup_cause || channel->state >= CS_HANGUP) {
		return SWITCH_STATUS_FALSE;
	}

	if (switch_channel_test_flag(channel, CF_ANSWERED)) {
		return SWITCH_STATUS_SUCCESS;
	}

	switch_core_media_check_dtls(channel->session, SWITCH_MEDIA_TYPE_AUDIO);

	if (channel->caller_profile && channel->caller_profile->times) {
		switch_mutex_lock(channel->profile_mutex);
		channel->caller_profile->times->answered = switch_micro_time_now();
		switch_mutex_unlock(channel->profile_mutex);
	}

	switch_channel_set_flag(channel, CF_ANSWERED);

	if (switch_true(switch_channel_get_variable(channel, "video_mirror_input"))) {
		switch_channel_set_flag(channel, CF_VIDEO_MIRROR_INPUT);
		//switch_channel_set_flag(channel, CF_VIDEO_DECODED_READ);
	}


	if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_ANSWER) == SWITCH_STATUS_SUCCESS) {
		switch_channel_event_set_data(channel, event);
		switch_event_fire(&event);
	}

	/* if we're the child of another channel and the other channel is in a blocking read they will never realize we have answered so send
	   a SWITCH_SIG_BREAK to interrupt any blocking reads on that channel
	 */
	if ((uuid = switch_channel_get_variable(channel, SWITCH_ORIGINATOR_VARIABLE))
		&& (other_session = switch_core_session_locate(uuid))) {
		switch_core_session_kill_channel(other_session, SWITCH_SIG_BREAK);
		switch_core_session_rwunlock(other_session);
	}

	if (switch_true(switch_channel_get_variable(channel, SWITCH_PASSTHRU_PTIME_MISMATCH_VARIABLE))) {
		switch_channel_set_flag(channel, CF_PASSTHRU_PTIME_MISMATCH);
	}

	if ((var = switch_channel_get_variable(channel, SWITCH_ENABLE_HEARTBEAT_EVENTS_VARIABLE))) {
		uint32_t seconds = 60;
		int tmp;

		if (switch_is_number(var)) {
			tmp = atoi(var);
			if (tmp > 0) {
				seconds = tmp;
			}
		} else if (!switch_true(var)) {
			seconds = 0;
		}

		if (seconds) {
			switch_core_session_enable_heartbeat(channel->session, seconds);
		}
	}

	switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ANSWER");
	switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_NOTICE, "Channel [%s] has been answered\n",
					  channel->name);


	if (switch_channel_get_variable(channel, "absolute_codec_string")) {
		/* inherit_codec == true will implicitly clear the absolute_codec_string
		   variable if used since it was the reason it was set in the first place and is no longer needed */
		if (switch_true(switch_channel_get_variable(channel, "inherit_codec"))) {
			switch_channel_set_variable(channel, "absolute_codec_string", NULL);
		}
	}

	switch_channel_execute_on(channel, SWITCH_CHANNEL_EXECUTE_ON_ANSWER_VARIABLE);

	if (!switch_channel_test_flag(channel, CF_EARLY_MEDIA)) {
		switch_channel_execute_on(channel, SWITCH_CHANNEL_EXECUTE_ON_MEDIA_VARIABLE);
		switch_channel_api_on(channel, SWITCH_CHANNEL_API_ON_MEDIA_VARIABLE);
	}

	switch_channel_api_on(channel, SWITCH_CHANNEL_API_ON_ANSWER_VARIABLE);

	switch_channel_presence(channel, "unknown", "answered", NULL);

	//switch_channel_audio_sync(channel);
			
	if (!switch_channel_test_flag(channel, CF_NO_RECOVER)) { 
		switch_core_recovery_track(channel->session);
	}
	
	switch_channel_set_callstate(channel, CCS_ACTIVE);

	send_ind(channel, SWITCH_MESSAGE_ANSWER_EVENT, file, func, line);

	switch_core_media_check_autoadj(channel->session);

	if (switch_channel_test_flag(channel, CF_RTT)) {
		switch_channel_set_flag_partner(channel, CF_RTT);
	}

	return SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE(switch_status_t) switch_channel_perform_answer(switch_channel_t *channel, const char *file, const char *func, int line)
{
	switch_core_session_message_t msg = { 0 };
	switch_status_t status = SWITCH_STATUS_SUCCESS;

	switch_assert(channel != NULL);

	if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
		return SWITCH_STATUS_SUCCESS;
	}

	if (channel->hangup_cause || channel->state >= CS_HANGUP) {
		return SWITCH_STATUS_FALSE;
	}

	if (switch_channel_test_flag(channel, CF_ANSWERED)) {
		return SWITCH_STATUS_SUCCESS;
	}

	msg.message_id = SWITCH_MESSAGE_INDICATE_ANSWER;
	msg.from = channel->name;
	status = switch_core_session_perform_receive_message(channel->session, &msg, file, func, line);


	if (status == SWITCH_STATUS_SUCCESS) {
		switch_channel_perform_mark_answered(channel, file, func, line);
		if (!switch_channel_test_flag(channel, CF_EARLY_MEDIA)) {
			switch_channel_audio_sync(channel);
		}
	} else {
		switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
	}


	if (switch_core_session_in_thread(channel->session) && !switch_channel_test_flag(channel, CF_PROXY_MODE) &&
		!switch_channel_test_flag(channel, CF_HAS_TEXT)) {
		const char *delay;

		if ((delay = switch_channel_get_variable(channel, "answer_delay"))) {
			uint32_t msec = atoi(delay);

			if (msec) {
				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(channel->session), SWITCH_LOG_DEBUG, "Answer delay for %u msec\n", msec);
				switch_ivr_sleep(channel->session, msec, SWITCH_TRUE, NULL);
			}
		}
	}

	return status;
}

#define resize(l) {\
	char *dp;\
	olen += (len + l + block);\
	cpos = c - data;\
	if ((dp = realloc(data, olen))) {\
	data = dp;\
	c = data + cpos;\
	memset(c, 0, olen - cpos);\
	}}                           \

SWITCH_DECLARE(char *) switch_channel_expand_variables_check(switch_channel_t *channel, const char *in, switch_event_t *var_list, switch_event_t *api_list, uint32_t recur)
{
	char *p, *c = NULL;
	char *data, *indup, *endof_indup;
	size_t sp = 0, len = 0, olen = 0, vtype = 0, br = 0, cpos, block = 128;
	char *cloned_sub_val = NULL, *sub_val = NULL, *expanded_sub_val = NULL;
	char *func_val = NULL, *sb = NULL;
	int nv = 0;

	if (recur > 100) {
		return (char *) in;
	}

	if (zstr(in)) {
		return (char *) in;
	}

	nv = switch_string_var_check_const(in) || switch_string_has_escaped_data(in);

	if (!nv) {
		return (char *) in;
	}


	nv = 0;
	olen = strlen(in) + 1;
	indup = strdup(in);
	endof_indup = end_of_p(indup) + 1;

	if ((data = malloc(olen))) {
		memset(data, 0, olen);
		c = data;
		for (p = indup; p && p < endof_indup && *p; p++) {
			int global = 0;
			vtype = 0;

			if (*p == '\\') {
				if (*(p + 1) == '$') {
					nv = 1;
					p++;
					if (*(p + 1) == '$') {
						p++;
					}
				} else if (*(p + 1) == '\'') {
					p++;
					continue;
				} else if (*(p + 1) == '\\') {
					if (len + 1 >= olen) {
						resize(1);
					}

					*c++ = *p++;
					len++;
					continue;
				}
			}

			if (*p == '$' && !nv) {

				if (*(p + 1) == '$') {
					p++;
					global++;
				}

				if (*(p + 1)) {
					if (*(p + 1) == '{') {
						vtype = global ? 3 : 1;
					} else {
						nv = 1;
					}
				} else {
					nv = 1;
				}
			}

			if (nv) {
				if (len + 1 >= olen) {
					resize(1);
				}

				*c++ = *p;
				len++;
				nv = 0;
				continue;
			}

			if (vtype) {
				char *s = p, *e, *vname, *vval = NULL;
				size_t nlen;

				s++;

				if ((vtype == 1 || vtype == 3) && *s == '{') {
					br = 1;
					s++;
				}

				e = s;
				vname = s;
				while (*e) {
					if (br == 1 && *e == '}') {
						br = 0;
						*e++ = '\0';
						break;
					}

					if (br > 0) {
						if (e != s && *e == '{') {
							br++;
						} else if (br > 1 && *e == '}') {
							br--;
						}
					}

					e++;
				}
				p = e > endof_indup ? endof_indup : e;

				vval = NULL;
				for(sb = vname; sb && *sb; sb++) {
					if (*sb == ' ') {
						vval = sb;
						break;
					} else if (*sb == '(') {
						vval = sb;
						br = 1;
						break;
					}
				}

				if (vval) {
					e = vval - 1;
					*vval++ = '\0';
					while (*e == ' ') {
						*e-- = '\0';
					}
					e = vval;

					while (e && *e) {
						if (*e == '(') {
							br++;
						} else if (br > 1 && *e == ')') {
							br--;
						} else if (br == 1 && *e == ')') {
							*e = '\0';
							break;
						}
						e++;
					}

					vtype = 2;
				}

				if (vtype == 1 || vtype == 3) {
					char *expanded = NULL;
					int offset = 0;
					int ooffset = 0;
					char *ptr;
					int idx = -1;

					if ((expanded = switch_channel_expand_variables_check(channel, (char *) vname, var_list, api_list, recur+1)) == vname) {
						expanded = NULL;
					} else {
						vname = expanded;
					}

					if ((ptr = strchr(vname, ':'))) {
						*ptr++ = '\0';
						offset = atoi(ptr);
						if ((ptr = strchr(ptr, ':'))) {
							ptr++;
							ooffset = atoi(ptr);
						}
					}

					if ((ptr = strchr(vname, '[')) && strchr(ptr, ']')) {
						*ptr++ = '\0';
						idx = atoi(ptr);
					}

					if ((sub_val = (char *) switch_channel_get_variable_dup(channel, vname, SWITCH_TRUE, idx))) {
						if (var_list && !switch_event_check_permission_list(var_list, vname)) {
							sub_val = "<Variable Expansion Permission Denied>";
						}

						if ((expanded_sub_val = switch_channel_expand_variables_check(channel, sub_val, var_list, api_list, recur+1)) == sub_val) {
							expanded_sub_val = NULL;
						} else {
							sub_val = expanded_sub_val;
						}

						if (offset || ooffset) {
							cloned_sub_val = strdup(sub_val);
							switch_assert(cloned_sub_val);
							sub_val = cloned_sub_val;
						}

						if (offset >= 0) {
							if ((size_t) offset > strlen(sub_val)) {
								*sub_val = '\0';
							} else {
								sub_val += offset;
							}
						} else if ((size_t) abs(offset) <= strlen(sub_val)) {
							sub_val = cloned_sub_val + (strlen(cloned_sub_val) + offset);
						}

						if (ooffset > 0 && (size_t) ooffset < strlen(sub_val)) {
							if ((ptr = (char *) sub_val + ooffset)) {
								*ptr = '\0';
							}
						}
					}

					switch_safe_free(expanded);
				} else {
					switch_stream_handle_t stream = { 0 };
					char *expanded = NULL;

					SWITCH_STANDARD_STREAM(stream);

					if (stream.data) {
						char *expanded_vname = NULL;

						if ((expanded_vname = switch_channel_expand_variables_check(channel, (char *) vname, var_list, api_list, recur+1)) == vname) {
							expanded_vname = NULL;
						} else {
							vname = expanded_vname;
						}

						if ((expanded = switch_channel_expand_variables_check(channel, vval, var_list, api_list, recur+1)) == vval) {
							expanded = NULL;
						} else {
							vval = expanded;
						}

						if (!switch_core_test_flag(SCF_API_EXPANSION) || (api_list && !switch_event_check_permission_list(api_list, vname))) {
							func_val = NULL;
							sub_val = "<API Execute Permission Denied>";
							free(stream.data);
						} else {
							if (switch_api_execute(vname, vval, channel->session, &stream) == SWITCH_STATUS_SUCCESS) {
								func_val = stream.data;
								sub_val = func_val;
							} else {
								free(stream.data);
							}
						}

						switch_safe_free(expanded);
						switch_safe_free(expanded_vname);

					} else {
						switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_CRIT, "Memory Error!\n");
						free(data);
						free(indup);
						return (char *) in;
					}
				}
				if ((nlen = sub_val ? strlen(sub_val) : 0)) {
					if (len + nlen >= olen) {
						resize(nlen);
					}

					len += nlen;
					strcat(c, sub_val);
					c += nlen;
				}

				switch_safe_free(func_val);
				switch_safe_free(cloned_sub_val);
				switch_safe_free(expanded_sub_val);
				sub_val = NULL;
				vname = NULL;
				br = 0;
			}

			if (sp) {
				if (len + 1 >= olen) {
					resize(1);
				}

				*c++ = ' ';
				sp = 0;
				len++;
			}

			if (*p == '$') {
				p--;
			} else {
				if (len + 1 >= olen) {
					resize(1);
				}

				*c++ = *p;
				len++;
			}
		}
	}
	free(indup);

	return data;
}

SWITCH_DECLARE(char *) switch_channel_build_param_string(switch_channel_t *channel, switch_caller_profile_t *caller_profile, const char *prefix)
{
	switch_stream_handle_t stream = { 0 };
	switch_size_t encode_len = 1024, new_len = 0;
	char *encode_buf = NULL;
	const char *prof[13] = { 0 }, *prof_names[13] = {
	0};
	char *e = NULL;
	switch_event_header_t *hi;
	uint32_t x = 0;

	SWITCH_STANDARD_STREAM(stream);

	if (prefix) {
		stream.write_function(&stream, "%s&", prefix);
	}

	encode_buf = malloc(encode_len);
	switch_assert(encode_buf);

	if (!caller_profile) {
		caller_profile = switch_channel_get_caller_profile(channel);
	}

	switch_assert(caller_profile != NULL);

	prof[0] = caller_profile->context;
	prof[1] = caller_profile->destination_number;
	prof[2] = caller_profile->caller_id_name;
	prof[3] = caller_profile->caller_id_number;
	prof[4] = caller_profile->network_addr;
	prof[5] = caller_profile->ani;
	prof[6] = caller_profile->aniii;
	prof[7] = caller_profile->rdnis;
	prof[8] = caller_profile->source;
	prof[9] = caller_profile->chan_name;
	prof[10] = caller_profile->uuid;
	prof[11] = caller_profile->transfer_source;

	prof_names[0] = "context";
	prof_names[1] = "destination_number";
	prof_names[2] = "caller_id_name";
	prof_names[3] = "caller_id_number";
	prof_names[4] = "network_addr";
	prof_names[5] = "ani";
	prof_names[6] = "aniii";
	prof_names[7] = "rdnis";
	prof_names[8] = "source";
	prof_names[9] = "chan_name";
	prof_names[10] = "uuid";
	prof_names[11] = "transfer_source";

	for (x = 0; prof[x]; x++) {
		if (zstr(prof[x])) {
			continue;
		}
		new_len = (strlen(prof[x]) * 3) + 1;
		if (encode_len < new_len) {
			char *tmp;

			encode_len = new_len;

			if (!(tmp = realloc(encode_buf, encode_len))) {
				abort();
			}

			encode_buf = tmp;
		}
		switch_url_encode(prof[x], encode_buf, encode_len);
		stream.write_function(&stream, "%s=%s&", prof_names[x], encode_buf);
	}

	if (channel->caller_profile->soft) {
		profile_node_t *pn;

		for(pn = channel->caller_profile->soft; pn; pn = pn->next) {
			char *var = pn->var;
			char *val = pn->val;

			new_len = (strlen((char *) var) * 3) + 1;
			if (encode_len < new_len) {
				char *tmp;

				encode_len = new_len;

				tmp = realloc(encode_buf, encode_len);
				switch_assert(tmp);
				encode_buf = tmp;
			}

			switch_url_encode((char *) val, encode_buf, encode_len);
			stream.write_function(&stream, "%s=%s&", (char *) var, encode_buf);

		}
	}

	if ((hi = switch_channel_variable_first(channel))) {
		for (; hi; hi = hi->next) {
			char *var = hi->name;
			char *val = hi->value;

			new_len = (strlen((char *) var) * 3) + 1;
			if (encode_len < new_len) {
				char *tmp;

				encode_len = new_len;

				tmp = realloc(encode_buf, encode_len);
				switch_assert(tmp);
				encode_buf = tmp;
			}

			switch_url_encode((char *) val, encode_buf, encode_len);
			stream.write_function(&stream, "%s=%s&", (char *) var, encode_buf);

		}
		switch_channel_variable_last(channel);
	}

	e = (char *) stream.data + (strlen((char *) stream.data) - 1);

	if (e && *e == '&') {
		*e = '\0';
	}

	switch_safe_free(encode_buf);

	return stream.data;
}

SWITCH_DECLARE(switch_status_t) switch_channel_pass_callee_id(switch_channel_t *channel, switch_channel_t *other_channel)
{
	int x = 0;

	switch_assert(channel);
	switch_assert(other_channel);

	switch_mutex_lock(channel->profile_mutex);
	switch_mutex_lock(other_channel->profile_mutex);

	if (!zstr(channel->caller_profile->callee_id_name)) {
		other_channel->caller_profile->callee_id_name = switch_core_strdup(other_channel->caller_profile->pool, channel->caller_profile->callee_id_name);
		x++;
	}

	if (!zstr(channel->caller_profile->callee_id_number)) {
		other_channel->caller_profile->callee_id_number = switch_core_strdup(other_channel->caller_profile->pool, channel->caller_profile->callee_id_number);
		x++;
	}

	switch_mutex_unlock(other_channel->profile_mutex);
	switch_mutex_unlock(channel->profile_mutex);

	return x ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
}

SWITCH_DECLARE(switch_status_t) switch_channel_get_variables(switch_channel_t *channel, switch_event_t **event)
{
	switch_status_t status;
	switch_mutex_lock(channel->profile_mutex);
	if (channel->variables) {
		status = switch_event_dup(event, channel->variables);
	} else {
		status = switch_event_create(event, SWITCH_EVENT_CHANNEL_DATA);
	}
	switch_mutex_unlock(channel->profile_mutex);
	return status;
}

SWITCH_DECLARE(switch_status_t) switch_channel_get_variables_prefix(switch_channel_t *channel, const char *prefix, switch_event_t **event)
{
	switch_status_t status = SWITCH_STATUS_SUCCESS;
	switch_event_t *vars;
	
	switch_event_create(&vars, SWITCH_EVENT_CHANNEL_DATA);
	
	switch_mutex_lock(channel->profile_mutex);
	if (channel->variables) {
		switch_event_header_t *hi;

		for (hi = channel->variables->headers; hi; hi = hi->next) {
			if (!strncmp(hi->name, prefix, strlen(prefix))) {
				switch_event_add_header_string(vars, SWITCH_STACK_BOTTOM, hi->name, hi->value);
			}
		}
	}
	switch_mutex_unlock(channel->profile_mutex);

	*event = vars;
	return status;
}

SWITCH_DECLARE(switch_core_session_t *) switch_channel_get_session(switch_channel_t *channel)
{
	switch_assert(channel);
	return channel->session;
}

SWITCH_DECLARE(switch_status_t) switch_channel_set_timestamps(switch_channel_t *channel)
{
	switch_status_t status = SWITCH_STATUS_SUCCESS;
	const char *cid_buf = NULL;
	switch_caller_profile_t *caller_profile;
	switch_app_log_t *app_log, *ap;
	char *last_app = NULL, *last_arg = NULL;
	char start[80] = "", resurrect[80] = "", answer[80] = "", hold[80],
		bridge[80] = "", progress[80] = "", progress_media[80] = "", end[80] = "", tmp[80] = "",
		profile_start[80] =	"";
	int32_t duration = 0, legbillsec = 0, billsec = 0, mduration = 0, billmsec = 0, legbillmsec = 0, progressmsec = 0, progress_mediamsec = 0;
	int32_t answersec = 0, answermsec = 0, waitsec = 0, waitmsec = 0;
	switch_time_t answerusec = 0;
	switch_time_t uduration = 0, legbillusec = 0, billusec = 0, progresssec = 0, progressusec = 0, progress_mediasec = 0, progress_mediausec = 0, waitusec = 0;
	time_t tt_created = 0, tt_answered = 0, tt_resurrected = 0, tt_bridged, tt_last_hold, tt_hold_accum,
		tt_progress = 0, tt_progress_media = 0, tt_hungup = 0, mtt_created = 0, mtt_answered = 0, mtt_bridged = 0,
		mtt_hungup = 0, tt_prof_created, mtt_progress = 0, mtt_progress_media = 0;
	void *pop;
	char dtstr[SWITCH_DTMF_LOG_LEN + 1] = "";
	int x = 0;

	switch_mutex_lock(channel->profile_mutex);

	if (switch_channel_test_flag(channel, CF_TIMESTAMP_SET)) {
		switch_mutex_unlock(channel->profile_mutex);
		return SWITCH_STATUS_FALSE;
	}

	if (!(caller_profile = channel->caller_profile) || !channel->variables) {
		switch_mutex_unlock(channel->profile_mutex);
		return SWITCH_STATUS_FALSE;
	}

	switch_channel_set_flag(channel, CF_TIMESTAMP_SET);

	if ((app_log = switch_core_session_get_app_log(channel->session))) {
		for (ap = app_log; ap && ap->next; ap = ap->next);
		last_app = ap->app;
		last_arg = ap->arg;
	}

	if (!zstr(caller_profile->caller_id_name)) {
		cid_buf = switch_core_session_sprintf(channel->session, "\"%s\" <%s>", caller_profile->caller_id_name,
											  switch_str_nil(caller_profile->caller_id_number));
	} else {
		cid_buf = caller_profile->caller_id_number;
	}

	while (x < SWITCH_DTMF_LOG_LEN && switch_queue_trypop(channel->dtmf_log_queue, &pop) == SWITCH_STATUS_SUCCESS) {
		switch_dtmf_t *dt = (switch_dtmf_t *) pop;

		if (dt) {
			dtstr[x++] = dt->digit;
			free(dt);
			dt = NULL;
		}
	}

	if (x) {
		const char *var = switch_channel_get_variable(channel, "digits_dialed_filter");
		char *digit_string = dtstr;
		char *X = NULL;
		switch_regex_t *re = NULL;
		char *substituted = NULL;

		if (!zstr(var)) {
			int proceed = 0;
			int ovector[30];

			if ((proceed = switch_regex_perform(dtstr, var, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) {
				int len = (strlen(dtstr) + strlen(var) + 10) * proceed;
				int i = 0;
				const char *replace = NULL;

				X = malloc(len);

				for (i = 0; i < proceed; i++) {
					if (pcre_get_substring(dtstr, ovector, proceed, i, &replace) >= 0) {
						if (replace) {
							switch_size_t plen = strlen(replace);
							memset(X, 'X', plen);
							*(X+plen) = '\0';
							
							switch_safe_free(substituted);
							substituted = switch_string_replace(substituted ? substituted : dtstr, replace, X);
							
							pcre_free_substring(replace);
						}
					}
				}

				if (!zstr(substituted)) {
					digit_string = substituted;
				}
			}
		}

		switch_channel_set_variable(channel, "digits_dialed", digit_string);
		switch_regex_safe_free(re);
		switch_safe_free(substituted);
		switch_safe_free(X);
	} else {
		switch_channel_set_variable(channel, "digits_dialed", "none");
	}

	if (caller_profile->times) {
		switch_time_exp_t tm;
		switch_size_t retsize;
		const char *fmt = "%Y-%m-%d %T";

		switch_time_exp_lt(&tm, caller_profile->times->created);
		switch_strftime_nocheck(start, &retsize, sizeof(start), fmt, &tm);
		switch_channel_set_variable(channel, "start_stamp", start);

		switch_time_exp_lt(&tm, caller_profile->times->profile_created);
		switch_strftime_nocheck(profile_start, &retsize, sizeof(profile_start), fmt, &tm);
		switch_channel_set_variable(channel, "profile_start_stamp", profile_start);

		if (caller_profile->times->answered) {
			switch_time_exp_lt(&tm, caller_profile->times->answered);
			switch_strftime_nocheck(answer, &retsize, sizeof(answer), fmt, &tm);
			switch_channel_set_variable(channel, "answer_stamp", answer);
		}

		if (caller_profile->times->bridged) {
			switch_time_exp_lt(&tm, caller_profile->times->bridged);
			switch_strftime_nocheck(bridge, &retsize, sizeof(bridge), fmt, &tm);
			switch_channel_set_variable(channel, "bridge_stamp", bridge);
		}

		if (caller_profile->times->last_hold) {
			switch_time_exp_lt(&tm, caller_profile->times->last_hold);
			switch_strftime_nocheck(hold, &retsize, sizeof(hold), fmt, &tm);
			switch_channel_set_variable(channel, "hold_stamp", hold);
		}

		if (caller_profile->times->resurrected) {
			switch_time_exp_lt(&tm, caller_profile->times->resurrected);
			switch_strftime_nocheck(resurrect, &retsize, sizeof(resurrect), fmt, &tm);
			switch_channel_set_variable(channel, "resurrect_stamp", resurrect);
		}

		if (caller_profile->times->progress) {
			switch_time_exp_lt(&tm, caller_profile->times->progress);
			switch_strftime_nocheck(progress, &retsize, sizeof(progress), fmt, &tm);
			switch_channel_set_variable(channel, "progress_stamp", progress);
		}

		if (caller_profile->times->progress_media) {
			switch_time_exp_lt(&tm, caller_profile->times->progress_media);
			switch_strftime_nocheck(progress_media, &retsize, sizeof(progress_media), fmt, &tm);
			switch_channel_set_variable(channel, "progress_media_stamp", progress_media);
		}

		if (channel->hold_record) {
			switch_hold_record_t *hr;
			switch_stream_handle_t stream = { 0 };

			SWITCH_STANDARD_STREAM(stream);

			stream.write_function(&stream, "{", SWITCH_VA_NONE);

			for (hr = channel->hold_record; hr; hr = hr->next) {
				stream.write_function(&stream, "{%"SWITCH_TIME_T_FMT",%"SWITCH_TIME_T_FMT"},", hr->on, hr->off);
			}
			end_of((char *)stream.data) = '}';

			switch_channel_set_variable(channel, "hold_events", (char *)stream.data);
			free(stream.data);
		}

		switch_time_exp_lt(&tm, caller_profile->times->hungup);
		switch_strftime_nocheck(end, &retsize, sizeof(end), fmt, &tm);
		switch_channel_set_variable(channel, "end_stamp", end);

		tt_created = (time_t) (caller_profile->times->created / 1000000);
		mtt_created = (time_t) (caller_profile->times->created / 1000);
		switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_created);
		switch_channel_set_variable(channel, "start_epoch", tmp);
		switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->created);
		switch_channel_set_variable(channel, "start_uepoch", tmp);

		tt_prof_created = (time_t) (caller_profile->times->profile_created / 1000000);
		switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_prof_created);
		switch_channel_set_variable(channel, "profile_start_epoch", tmp);
		switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->profile_created);
		switch_channel_set_variable(channel, "profile_start_uepoch", tmp);

		tt_answered = (time_t) (caller_profile->times->answered / 1000000);
		mtt_answered = (time_t) (caller_profile->times->answered / 1000);
		switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_answered);
		switch_channel_set_variable(channel, "answer_epoch", tmp);
		switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->answered);
		switch_channel_set_variable(channel, "answer_uepoch", tmp);

		tt_bridged = (time_t) (caller_profile->times->bridged / 1000000);
		mtt_bridged = (time_t) (caller_profile->times->bridged / 1000);
		switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_bridged);
		switch_channel_set_variable(channel, "bridge_epoch", tmp);
		switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->bridged);
		switch_channel_set_variable(channel, "bridge_uepoch", tmp);

		tt_last_hold = (time_t) (caller_profile->times->last_hold / 1000000);
		switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_last_hold);
		switch_channel_set_variable(channel, "last_hold_epoch", tmp);
		switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->last_hold);
		switch_channel_set_variable(channel, "last_hold_uepoch", tmp);

		tt_hold_accum = (time_t) (caller_profile->times->hold_accum / 1000000);
		switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_hold_accum);
		switch_channel_set_variable(channel, "hold_accum_seconds", tmp);
		switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->hold_accum);
		switch_channel_set_variable(channel, "hold_accum_usec", tmp);
		switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->hold_accum / 1000);
		switch_channel_set_variable(channel, "hold_accum_ms", tmp);

		tt_resurrected = (time_t) (caller_profile->times->resurrected / 1000000);
		switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_resurrected);
		switch_channel_set_variable(channel, "resurrect_epoch", tmp);
		switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->resurrected);
		switch_channel_set_variable(channel, "resurrect_uepoch", tmp);

		tt_progress = (time_t) (caller_profile->times->progress / 1000000);
		mtt_progress = (time_t) (caller_profile->times->progress / 1000);
		switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_progress);
		switch_channel_set_variable(channel, "progress_epoch", tmp);
		switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->progress);
		switch_channel_set_variable(channel, "progress_uepoch", tmp);

		tt_progress_media = (time_t) (caller_profile->times->progress_media / 1000000);
		mtt_progress_media = (time_t) (caller_profile->times->progress_media / 1000);
		switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_progress_media);
		switch_channel_set_variable(channel, "progress_media_epoch", tmp);
		switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->progress_media);
		switch_channel_set_variable(channel, "progress_media_uepoch", tmp);

		tt_hungup = (time_t) (caller_profile->times->hungup / 1000000);
		mtt_hungup = (time_t) (caller_profile->times->hungup / 1000);
		switch_snprintf(tmp, sizeof(tmp), "%" TIME_T_FMT, tt_hungup);
		switch_channel_set_variable(channel, "end_epoch", tmp);
		switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, caller_profile->times->hungup);
		switch_channel_set_variable(channel, "end_uepoch", tmp);

		duration = (int32_t) (tt_hungup - tt_created);
		mduration = (int32_t) (mtt_hungup - mtt_created);
		uduration = caller_profile->times->hungup - caller_profile->times->created;

		if (caller_profile->times->bridged > caller_profile->times->created) {
			waitsec = (int32_t) (tt_bridged - tt_created);
			waitmsec = (int32_t) (mtt_bridged - mtt_created);
			waitusec = caller_profile->times->bridged - caller_profile->times->created;
		} else {
			waitsec = 0;
			waitmsec = 0;
			waitusec = 0;
		}

		if (caller_profile->times->answered) {
			billsec = (int32_t) (tt_hungup - tt_answered);
			billmsec = (int32_t) (mtt_hungup - mtt_answered);
			billusec = caller_profile->times->hungup - caller_profile->times->answered;

			legbillsec = (int32_t) (tt_hungup - tt_created);
			legbillmsec = (int32_t) (mtt_hungup - mtt_created);
			legbillusec = caller_profile->times->hungup - caller_profile->times->created;

			answersec = (int32_t) (tt_answered - tt_created);
			answermsec = (int32_t) (mtt_answered - mtt_created);
			answerusec = caller_profile->times->answered - caller_profile->times->created;
		}

		if (caller_profile->times->progress) {
			progresssec = (int32_t) (tt_progress - tt_created);
			progressmsec = (int32_t) (mtt_progress - mtt_created);
			progressusec = caller_profile->times->progress - caller_profile->times->created;
		}

		if (caller_profile->times->progress_media) {
			progress_mediasec = (int32_t) (tt_progress_media - tt_created);
			progress_mediamsec = (int32_t) (mtt_progress_media - mtt_created);
			progress_mediausec = caller_profile->times->progress_media - caller_profile->times->created;
		}

	}

	switch_channel_set_variable(channel, "last_app", last_app);
	switch_channel_set_variable(channel, "last_arg", last_arg);
	switch_channel_set_variable(channel, "caller_id", cid_buf);

	switch_snprintf(tmp, sizeof(tmp), "%d", duration);
	switch_channel_set_variable(channel, "duration", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%d", billsec);
	switch_channel_set_variable(channel, "billsec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%"SWITCH_TIME_T_FMT, progresssec);
	switch_channel_set_variable(channel, "progresssec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%d", answersec);
	switch_channel_set_variable(channel, "answersec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%d", waitsec);
	switch_channel_set_variable(channel, "waitsec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%"SWITCH_TIME_T_FMT, progress_mediasec);
	switch_channel_set_variable(channel, "progress_mediasec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%d", legbillsec);
	switch_channel_set_variable(channel, "flow_billsec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%d", mduration);
	switch_channel_set_variable(channel, "mduration", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%d", billmsec);
	switch_channel_set_variable(channel, "billmsec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%d", progressmsec);
	switch_channel_set_variable(channel, "progressmsec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%d", answermsec);
	switch_channel_set_variable(channel, "answermsec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%d", waitmsec);
	switch_channel_set_variable(channel, "waitmsec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%d", progress_mediamsec);
	switch_channel_set_variable(channel, "progress_mediamsec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%d", legbillmsec);
	switch_channel_set_variable(channel, "flow_billmsec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, uduration);
	switch_channel_set_variable(channel, "uduration", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, billusec);
	switch_channel_set_variable(channel, "billusec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, progressusec);
	switch_channel_set_variable(channel, "progressusec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, answerusec);
	switch_channel_set_variable(channel, "answerusec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, waitusec);
	switch_channel_set_variable(channel, "waitusec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, progress_mediausec);
	switch_channel_set_variable(channel, "progress_mediausec", tmp);

	switch_snprintf(tmp, sizeof(tmp), "%" SWITCH_TIME_T_FMT, legbillusec);
	switch_channel_set_variable(channel, "flow_billusec", tmp);

	switch_mutex_unlock(channel->profile_mutex);

	return status;
}

SWITCH_DECLARE(const char *) switch_channel_get_partner_uuid_copy(switch_channel_t *channel, char *buf, switch_size_t blen)
{
	const char *uuid = NULL;

	switch_mutex_lock(channel->profile_mutex);
	if (!(uuid = switch_channel_get_variable_dup(channel, SWITCH_SIGNAL_BOND_VARIABLE, SWITCH_TRUE, -1))) {
		uuid = switch_channel_get_variable_dup(channel, SWITCH_ORIGINATE_SIGNAL_BOND_VARIABLE, SWITCH_TRUE, -1);
	}

	if (uuid) {
		strncpy(buf, uuid, blen);
		uuid = (const char *) buf;
	}
	switch_mutex_unlock(channel->profile_mutex);

	return uuid;
}


SWITCH_DECLARE(const char *) switch_channel_get_partner_uuid(switch_channel_t *channel)
{
	const char *uuid = NULL;

	if (!(uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE))) {
		uuid = switch_channel_get_variable(channel, SWITCH_ORIGINATE_SIGNAL_BOND_VARIABLE);
	}

	return uuid;
}

SWITCH_DECLARE(void) switch_channel_handle_cause(switch_channel_t *channel, switch_call_cause_t cause)
{
	switch_core_session_t *session = channel->session;
	const char *transfer_on_fail = NULL;
	char *tof_data = NULL;
	char *tof_array[4] = { 0 };
	//int tof_arrayc = 0;

	if (!switch_channel_up_nosig(channel)) {
		return;
	}

	transfer_on_fail = switch_channel_get_variable(channel, "transfer_on_fail");
	tof_data = switch_core_session_strdup(session, transfer_on_fail);
	switch_split(tof_data, ' ', tof_array);
   	transfer_on_fail = tof_array[0];

	/*
	   if the variable continue_on_fail is set it can be:
	   'true' to continue on all failures.
	   'false' to not continue.
	   A list of codes either names or numbers eg "user_busy,normal_temporary_failure,603"
	   failure_causes acts as the opposite version
	   EXCEPTION... ATTENDED_TRANSFER never is a reason to continue.......
	*/
	if (cause != SWITCH_CAUSE_ATTENDED_TRANSFER) {
		const char *continue_on_fail = NULL, *failure_causes = NULL;

		continue_on_fail = switch_channel_get_variable(channel, "continue_on_fail");
		failure_causes = switch_channel_get_variable(channel, "failure_causes");

		if (continue_on_fail || failure_causes) {
			const char *cause_str;
			char cause_num[35] = "";

			cause_str = switch_channel_cause2str(cause);
			switch_snprintf(cause_num, sizeof(cause_num), "%u", cause);

			if (failure_causes) {
				char *lbuf = switch_core_session_strdup(session, failure_causes);
				char *argv[256] = { 0 };
				int argc = switch_separate_string(lbuf, ',', argv, (sizeof(argv) / sizeof(argv[0])));
				int i, x = 0;

				for (i = 0; i < argc; i++) {
					if (!strcasecmp(argv[i], cause_str) || !strcasecmp(argv[i], cause_num)) {
						x++;
						break;
					}
				}
				if (!x) {
					switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
									  "Failure causes [%s]:  Cause: %s\n", failure_causes, cause_str);
					return;
				}
			}

			if (continue_on_fail) {
				if (switch_true(continue_on_fail)) {
					return;
				} else {
					char *lbuf = switch_core_session_strdup(session, continue_on_fail);
					char *argv[256] = { 0 };
					int argc = switch_separate_string(lbuf, ',', argv, (sizeof(argv) / sizeof(argv[0])));
					int i;

					for (i = 0; i < argc; i++) {
						if (!strcasecmp(argv[i], cause_str) || !strcasecmp(argv[i], cause_num)) {
							switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
											  "Continue on fail [%s]:  Cause: %s\n", continue_on_fail, cause_str);
							return;
						}
					}
				}
			}
		} else {
			/* no answer is *always* a reason to continue */
			if (cause == SWITCH_CAUSE_NO_ANSWER || cause == SWITCH_CAUSE_NO_USER_RESPONSE || cause == SWITCH_CAUSE_ORIGINATOR_CANCEL) {
				return;
			}
		}

		if (transfer_on_fail || failure_causes) {
			const char *cause_str;
			char cause_num[35] = "";

			cause_str = switch_channel_cause2str(cause);
			switch_snprintf(cause_num, sizeof(cause_num), "%u", cause);

			if ((tof_array[1] == NULL ) || (!strcasecmp(tof_array[1], "auto_cause"))){
				tof_array[1] = (char *) cause_str;
			}

			if (failure_causes) {
				char *lbuf = switch_core_session_strdup(session, failure_causes);
				char *argv[256] = { 0 };
				int argc = switch_separate_string(lbuf, ',', argv, (sizeof(argv) / sizeof(argv[0])));
				int i, x = 0;

				for (i = 0; i < argc; i++) {
					if (!strcasecmp(argv[i], cause_str) || !strcasecmp(argv[i], cause_num)) {
						x++;
						break;
					}
				}
				if (!x) {
					switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
									  "Failure causes [%s]:  Cause: %s\n", failure_causes, cause_str);

					switch_ivr_session_transfer(session, tof_array[1], tof_array[2], tof_array[3]);
				}
			}

			if (transfer_on_fail) {
				if (switch_true(transfer_on_fail)) {
					return;
				} else {
					char *lbuf = switch_core_session_strdup(session, transfer_on_fail);
					char *argv[256] = { 0 };
					int argc = switch_separate_string(lbuf, ',', argv, (sizeof(argv) / sizeof(argv[0])));
					int i;

					for (i = 0; i < argc; i++) {
						if (!strcasecmp(argv[i], cause_str) || !strcasecmp(argv[i], cause_num)) {
							switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
											  "Transfer on fail [%s]:  Cause: %s\n", transfer_on_fail, cause_str);
							switch_ivr_session_transfer(session, tof_array[1], tof_array[2], tof_array[3]);
						}
					}
				}
			}
		}
	}


	if (!switch_channel_test_flag(channel, CF_TRANSFER) && !switch_channel_test_flag(channel, CF_CONFIRM_BLIND_TRANSFER) &&
		switch_channel_get_state(channel) != CS_ROUTING) {
		switch_channel_hangup(channel, cause);
	}
}

SWITCH_DECLARE(void) switch_channel_global_init(switch_memory_pool_t *pool)
{
	memset(&globals, 0, sizeof(globals));
	globals.pool = pool;

	switch_mutex_init(&globals.device_mutex, SWITCH_MUTEX_NESTED, pool);
	switch_core_hash_init(&globals.device_hash);
}

SWITCH_DECLARE(void) switch_channel_global_uninit(void)
{
	switch_core_hash_destroy(&globals.device_hash);
}


static void fetch_device_stats(switch_device_record_t *drec)
{
	switch_device_node_t *np;


	memset(&drec->stats, 0, sizeof(switch_device_stats_t));

	switch_mutex_lock(drec->mutex);
	for(np = drec->uuid_list; np; np = np->next) {
		drec->stats.total++;
		if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
			drec->stats.total_in++;
		} else {
			drec->stats.total_out++;
		}

		if (!np->hup_profile) {
			drec->stats.offhook++;
			if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
				drec->stats.offhook_in++;
			} else {
				drec->stats.offhook_out++;
			}

			if (np->callstate == CCS_HELD) {
				drec->stats.held++;
				if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
					drec->stats.held_in++;
				} else {
					drec->stats.held_out++;
				}
			} else if (np->callstate == CCS_UNHELD) {
				drec->stats.unheld++;
				if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
					drec->stats.unheld_in++;
				} else {
					drec->stats.unheld_out++;
				}
			} else {
				if (np->callstate == CCS_EARLY) {
					drec->stats.early++;
					if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
						drec->stats.early_in++;
					} else {
						drec->stats.early_out++;
					}
				} else if (np->callstate == CCS_RINGING) {
					drec->stats.ringing++;
					if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
						drec->stats.ringing_in++;
					} else {
						drec->stats.ringing_out++;
					}
				} else if (np->callstate == CCS_RING_WAIT) {
					drec->stats.ring_wait++;
				} else if (np->callstate == CCS_HANGUP) {
					drec->stats.hup++;
					if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
						drec->stats.hup_in++;
					} else {
						drec->stats.hup_out++;
					}
				} else if (np->callstate != CCS_DOWN) {
					drec->stats.active++;
					if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
						drec->stats.active_in++;
					} else {
						drec->stats.active_out++;
					}
				}
			}
		} else {
			drec->stats.hup++;
			if (np->direction == SWITCH_CALL_DIRECTION_INBOUND) {
				drec->stats.hup_in++;
			} else {
				drec->stats.hup_out++;
			}
		}
	}
	switch_mutex_unlock(drec->mutex);

}

SWITCH_DECLARE(void) switch_channel_clear_device_record(switch_channel_t *channel)
{
	switch_memory_pool_t *pool;
	int sanity = 100;
	switch_device_node_t *np;
	switch_event_t *event;

	if (!channel->device_node || !switch_channel_test_flag(channel, CF_FINAL_DEVICE_LEG)) {
		return;
	}

	while(--sanity && channel->device_node->parent->refs) {
		switch_yield(100000);
	}

	switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "Destroying device cdr %s on device [%s]\n",
					  channel->device_node->parent->uuid,
					  channel->device_node->parent->device_id);

	if (switch_event_create(&event, SWITCH_EVENT_CALL_DETAIL) == SWITCH_STATUS_SUCCESS) {
		int x = 0;
		char prefix[80] = "";

		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Type", "device");
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Device-ID", channel->device_node->parent->device_id);

		switch_mutex_lock(channel->device_node->parent->mutex);
		for(np = channel->device_node->parent->uuid_list; np; np = np->next) {
			switch_snprintf(prefix, sizeof(prefix), "Call-%d", ++x);
			switch_caller_profile_event_set_data(np->hup_profile, prefix, event);
		}
		switch_mutex_unlock(channel->device_node->parent->mutex);

		switch_event_fire(&event);
	}

	switch_mutex_lock(channel->device_node->parent->mutex);
	for(np = channel->device_node->parent->uuid_list; np; np = np->next) {
		if (np->xml_cdr) {
			switch_xml_free(np->xml_cdr);
		}
		if (np->event) {
			switch_event_destroy(&np->event);
		}
	}
	switch_mutex_unlock(channel->device_node->parent->mutex);

	pool = channel->device_node->parent->pool;

	switch_mutex_lock(globals.device_mutex);
	switch_core_destroy_memory_pool(&pool);

	switch_mutex_unlock(globals.device_mutex);


}

SWITCH_DECLARE(void) switch_channel_process_device_hangup(switch_channel_t *channel)
{

	switch_channel_check_device_state(channel, channel->callstate);
	process_device_hup(channel);

}

static void process_device_hup(switch_channel_t *channel)
{
	switch_hold_record_t *hr, *newhr, *last = NULL;
	switch_device_record_t *drec = NULL;
	switch_device_node_t *node;

	if (!channel->device_node) {
		return;
	}

	switch_mutex_lock(globals.device_mutex);
	node = channel->device_node;
	drec = channel->device_node->parent;

	node->hup_profile = switch_caller_profile_dup(drec->pool, channel->caller_profile);
	fetch_device_stats(drec);

	switch_ivr_generate_xml_cdr(channel->session, &node->xml_cdr);
	if (switch_event_create(&node->event, SWITCH_EVENT_CALL_DETAIL) == SWITCH_STATUS_SUCCESS) {
		switch_channel_event_set_extended_data(channel, node->event);
	}

	for (hr = channel->hold_record; hr; hr = hr->next) {
		newhr = switch_core_alloc(drec->pool, sizeof(*newhr));
		newhr->on = hr->on;
		newhr->off = hr->off;

		if (hr->uuid) {
			newhr->uuid = switch_core_strdup(drec->pool, hr->uuid);
		}

		if (!node->hold_record) {
			node->hold_record = newhr;
		} else if (last) {
			last->next = newhr;
		}

		last = newhr;
	}

	if (!drec->stats.offhook) { /* this is final call */

		switch_core_hash_delete(globals.device_hash, drec->device_id);
		switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "Processing last call from device [%s]\n",
						  drec->device_id);
		switch_channel_set_flag(channel, CF_FINAL_DEVICE_LEG);
	} else {
		channel->device_node = NULL;
	}

	drec->refs--;

	switch_mutex_unlock(globals.device_mutex);

}

static void switch_channel_check_device_state(switch_channel_t *channel, switch_channel_callstate_t callstate)
{
	switch_device_record_t *drec = NULL;
	switch_device_state_binding_t *ptr = NULL;
	switch_event_t *event = NULL;

	if (!channel->device_node) {
		return;
	}

	drec = channel->device_node->parent;

	switch_mutex_lock(globals.device_mutex);
	switch_mutex_lock(drec->mutex);

	fetch_device_stats(drec);

	if (drec->state != SDS_HANGUP) {
		if (drec->stats.offhook == 0 || drec->stats.hup == drec->stats.total) {
			drec->state = SDS_HANGUP;
		} else {
			if (drec->stats.active == 0) {
				if ((drec->stats.ringing_out + drec->stats.early_out) > 0 || drec->stats.ring_wait > 0) {
					drec->state = SDS_RINGING;
				} else {
					if (drec->stats.held > 0) {
						drec->state = SDS_HELD;
					} else if (drec->stats.unheld > 0) {
						drec->state = SDS_UNHELD;
					} else {
						drec->state = SDS_DOWN;
					}
				}
			} else if (drec->stats.active == 1) {
				drec->state = SDS_ACTIVE;
			} else {
				drec->state = SDS_ACTIVE_MULTI;
			}
		}
	}

	if ((drec->state == SDS_DOWN && drec->last_state == SDS_DOWN) || (drec->state == SDS_HANGUP && drec->last_state == SDS_HANGUP)) {
		switch_mutex_unlock(drec->mutex);
		switch_mutex_unlock(globals.device_mutex);
		return;
	}

	if (!drec->call_start) {
		drec->call_start = switch_micro_time_now();
	}

	switch(drec->state) {
	case SDS_RINGING:
		if (!drec->ring_start) {
			drec->ring_start = switch_micro_time_now();
			drec->ring_stop = 0;
		}
		break;
	case SDS_ACTIVE:
	case SDS_ACTIVE_MULTI:
		if (!drec->active_start) {
			drec->active_start = switch_micro_time_now();
			drec->active_stop = 0;
		}
		break;
	case SDS_HELD:
		if (!drec->hold_start) {
			drec->hold_start = switch_micro_time_now();
			drec->hold_stop = 0;
		}
		break;
	default:
		break;
	}

	if (callstate != CCS_UNHELD && drec->active_start && drec->state != SDS_ACTIVE && drec->state != SDS_ACTIVE_MULTI) {
		drec->active_stop = switch_micro_time_now();
	}

	if (drec->ring_start && !drec->ring_stop && drec->state != SDS_RINGING) {
		drec->ring_stop = switch_micro_time_now();
	}

	if (drec->hold_start && !drec->hold_stop && drec->state != SDS_HELD) {
		drec->hold_stop = switch_micro_time_now();
	}


	if (switch_event_create(&event, SWITCH_EVENT_DEVICE_STATE) == SWITCH_STATUS_SUCCESS) {
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Device-ID", drec->device_id);
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Last-Device-State", switch_channel_device_state2str(drec->last_state));
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Device-State", switch_channel_device_state2str(drec->state));
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Device-Call-State", switch_channel_callstate2str(callstate));
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Total-Legs", "%u", drec->stats.total);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-Offhook", "%u", drec->stats.offhook);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-Ringing", "%u", drec->stats.ringing);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-Early", "%u", drec->stats.early);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-Active", "%u", drec->stats.active);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-Held", "%u", drec->stats.held);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-UnHeld", "%u", drec->stats.unheld);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-Hup", "%u", drec->stats.hup);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Talk-Time-Start-Uepoch", "%"SWITCH_TIME_T_FMT, drec->active_start);
		if (drec->active_stop) {
			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Talk-Time-Stop-Uepoch", "%"SWITCH_TIME_T_FMT, drec->active_stop);
			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Talk-Time-Milliseconds", "%u", (uint32_t)(drec->active_stop - drec->active_start) / 1000);
		}
	}

	switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG1,
					  "%s device: %s\nState: %s Dev State: %s/%s Total:%u Offhook:%u "
					  "Ringing:%u Early:%u Active:%u Held:%u Unheld:%u Hungup:%u Dur: %u Ringtime: %u Holdtime: %u %s\n",
					  switch_channel_get_name(channel),
					  drec->device_id,
					  switch_channel_callstate2str(callstate),
					  switch_channel_device_state2str(drec->last_state),
					  switch_channel_device_state2str(drec->state),
					  drec->stats.total,
					  drec->stats.offhook,
					  drec->stats.ringing,
					  drec->stats.early,
					  drec->stats.active,
					  drec->stats.held,
					  drec->stats.unheld,
					  drec->stats.hup,
					  drec->active_stop ? (uint32_t)(drec->active_stop - drec->active_start) / 1000 : 0,
					  drec->ring_stop ? (uint32_t)(drec->ring_stop - drec->ring_start) / 1000 : 0,
					  drec->hold_stop ? (uint32_t)(drec->hold_stop - drec->hold_start) / 1000 : 0,
					  switch_channel_test_flag(channel, CF_FINAL_DEVICE_LEG) ? "FINAL LEG" : "");

	for (ptr = globals.device_bindings; ptr; ptr = ptr->next) {
		ptr->function(channel->session, callstate, drec);
	}

	drec->last_stats = drec->stats;

	if (drec->active_stop) {
		drec->active_start = drec->active_stop = 0;
		if (drec->state == SDS_ACTIVE || drec->state == SDS_ACTIVE_MULTI) {
			drec->active_start = switch_micro_time_now();
		}
	}

	if (drec->hold_stop) {
		drec->hold_start = drec->hold_stop = 0;
		if (drec->state == SDS_HELD) {
			drec->hold_start = switch_micro_time_now();
		}
	}

	if (drec->ring_stop) {
		drec->ring_start = drec->ring_stop = 0;
		if (drec->state == SDS_RINGING) {
			drec->ring_start = switch_micro_time_now();
		}
	}

	drec->last_call_time = switch_micro_time_now();

	drec->last_state = drec->state;

	switch_mutex_unlock(drec->mutex);
	switch_mutex_unlock(globals.device_mutex);


	if (event) {
		switch_event_fire(&event);
	}

}

/* assumed to be called under a lock */
static void add_uuid(switch_device_record_t *drec, switch_channel_t *channel)
{
	switch_device_node_t *node;

	switch_assert(drec);

	switch_channel_set_flag(channel, CF_DEVICE_LEG);
	node = switch_core_alloc(drec->pool, sizeof(*node));

	node->uuid = switch_core_strdup(drec->pool, switch_core_session_get_uuid(channel->session));
	node->parent = drec;
	node->callstate = channel->callstate;
	node->direction = channel->logical_direction == SWITCH_CALL_DIRECTION_INBOUND ? SWITCH_CALL_DIRECTION_OUTBOUND : SWITCH_CALL_DIRECTION_INBOUND;

	channel->device_node = node;

	if (!drec->uuid_list) {
		drec->uuid_list = node;
		drec->uuid = node->uuid;
	} else {
		drec->uuid_tail->next = node;
	}

	drec->uuid_tail = node;
	drec->refs++;
}

static switch_status_t create_device_record(switch_device_record_t **drecp, const char *device_id)
{
	switch_device_record_t *drec;
	switch_memory_pool_t *pool;

	switch_assert(drecp);

	switch_core_new_memory_pool(&pool);
	drec = switch_core_alloc(pool, sizeof(*drec));
	drec->pool = pool;
	drec->device_id = switch_core_strdup(drec->pool, device_id);
	switch_mutex_init(&drec->mutex, SWITCH_MUTEX_NESTED, drec->pool);

	*drecp = drec;

	return SWITCH_STATUS_SUCCESS;
}


SWITCH_DECLARE(const char *) switch_channel_set_device_id(switch_channel_t *channel, const char *device_id)
{
	switch_device_record_t *drec;

	if (channel->device_node) {
		return NULL;
	}

	channel->device_id = switch_core_session_strdup(channel->session, device_id);

	switch_mutex_lock(globals.device_mutex);

	if (!(drec = switch_core_hash_find(globals.device_hash, channel->device_id))) {
		create_device_record(&drec, channel->device_id);
		switch_core_hash_insert(globals.device_hash, drec->device_id, drec);
	}

	add_uuid(drec, channel);

	switch_mutex_unlock(globals.device_mutex);

	switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "Setting DEVICE ID to [%s]\n", device_id);

	switch_channel_check_device_state(channel, channel->callstate);

	return device_id;
}

SWITCH_DECLARE(switch_device_record_t *) switch_channel_get_device_record(switch_channel_t *channel)
{
	if (channel->device_node) {
		switch_mutex_lock(channel->device_node->parent->mutex);
		return channel->device_node->parent;
	}

	return NULL;
}

SWITCH_DECLARE(void) switch_channel_release_device_record(switch_device_record_t **drecp)
{
	if (drecp && *drecp) {
		switch_mutex_unlock((*drecp)->mutex);
		*drecp = NULL;
	}
}

SWITCH_DECLARE(switch_status_t) switch_channel_bind_device_state_handler(switch_device_state_function_t function, void *user_data)
{
	switch_device_state_binding_t *binding = NULL, *ptr = NULL;
	assert(function != NULL);

	if (!(binding = (switch_device_state_binding_t *) switch_core_alloc(globals.pool, sizeof(*binding)))) {
		return SWITCH_STATUS_MEMERR;
	}

	binding->function = function;
	binding->user_data = user_data;

	switch_mutex_lock(globals.device_mutex);
	for (ptr = globals.device_bindings; ptr && ptr->next; ptr = ptr->next);

	if (ptr) {
		ptr->next = binding;
	} else {
		globals.device_bindings = binding;
	}

	switch_mutex_unlock(globals.device_mutex);

	return SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE(switch_status_t) switch_channel_unbind_device_state_handler(switch_device_state_function_t function)
{
	switch_device_state_binding_t *ptr, *last = NULL;
	switch_status_t status = SWITCH_STATUS_FALSE;

	switch_mutex_lock(globals.device_mutex);
	for (ptr = globals.device_bindings; ptr; ptr = ptr->next) {
		if (ptr->function == function) {
			status = SWITCH_STATUS_SUCCESS;

			if (last) {
				last->next = ptr->next;
			} else {
				globals.device_bindings = ptr->next;
				last = NULL;
				continue;
			}
		}
		last = ptr;
	}
	switch_mutex_unlock(globals.device_mutex);

	return status;
}

SWITCH_DECLARE(switch_status_t) switch_channel_pass_sdp(switch_channel_t *from_channel, switch_channel_t *to_channel, const char *sdp)
{
	switch_status_t status = SWITCH_STATUS_FALSE;
	char *use_sdp = (char *) sdp;
	char *patched_sdp = NULL;

	if (!switch_channel_get_variable(to_channel, SWITCH_B_SDP_VARIABLE)) {
		const char *var;

		if ((var = switch_channel_get_variable(from_channel, "bypass_media_sdp_filter"))) {
			if ((patched_sdp = switch_core_media_process_sdp_filter(use_sdp, var, from_channel->session))) {
				use_sdp = patched_sdp;
			}
		}

		switch_channel_set_variable(to_channel, SWITCH_B_SDP_VARIABLE, use_sdp);
	}

	switch_safe_free(patched_sdp);

	return status;
}

/* 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 noet:
 */
