/*
 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 * Copyright (C) 2005-2014, 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>
 *
 * switch_ivr_bridge.c -- IVR Library
 *
 */

#include <switch.h>
#define DEFAULT_LEAD_FRAMES 10

static const switch_state_handler_table_t audio_bridge_peer_state_handlers;
static void cleanup_proxy_mode_a(switch_core_session_t *session);
static void cleanup_proxy_mode_b(switch_core_session_t *session);

/* Bridge Related Stuff*/
/*********************************************************************************/

struct vid_helper {
	switch_core_session_t *session_a;
	switch_core_session_t *session_b;
	int up;
};


static void text_bridge_thread(switch_core_session_t *session, void *obj)
{
	struct vid_helper *vh = obj;
	switch_status_t status;
	switch_frame_t *read_frame = 0;
	switch_channel_t *channel = switch_core_session_get_channel(vh->session_a);
	switch_channel_t *b_channel = switch_core_session_get_channel(vh->session_b);
	switch_buffer_t *text_buffer = NULL;
	switch_size_t text_framesize = 1024, inuse = 0;
	unsigned char *text_framedata = NULL;
	switch_frame_t frame = { 0 };

	switch_buffer_create_dynamic(&text_buffer, 512, 1024, 0);
	switch_zmalloc(text_framedata, 1024);
	text_framesize = 1024;

	vh->up = 1;

	while (switch_channel_up_nosig(channel) && switch_channel_up_nosig(b_channel) && vh->up == 1) {
		status = switch_core_session_read_text_frame(vh->session_a, &read_frame, SWITCH_IO_FLAG_NONE, 0);

		if (!SWITCH_READ_ACCEPTABLE(status)) {
			switch_core_session_write_text_frame(vh->session_a, NULL, 0, 0);
			continue;
		}

		if (!switch_channel_test_flag(channel, CF_TEXT_LINE_BASED) && switch_channel_test_flag(b_channel, CF_TEXT_LINE_BASED)) {
			if (read_frame->data && read_frame->datalen && !switch_test_flag(read_frame, SFF_CNG)) {
				switch_buffer_write(text_buffer, read_frame->data, read_frame->datalen);
			}

			inuse = switch_buffer_inuse(text_buffer);

			if (inuse && (switch_channel_test_flag(channel, CF_TEXT_IDLE) || switch_test_flag(read_frame, SFF_TEXT_LINE_BREAK))) {
				int bytes;

				if (inuse + 4 > text_framesize) {
					void *tmp = malloc(inuse + 1024);

					switch_assert(tmp);
					memcpy(tmp, text_framedata, text_framesize);

					text_framesize = inuse + 1024;

					free(text_framedata);
					text_framedata = tmp;
				}

				bytes = switch_buffer_read(text_buffer, text_framedata, inuse);

				/* need to strip the unicode line feed because Blink ignores the message, need to see if that is a thing with other msrp clients */
				if (switch_test_flag(read_frame, SFF_TEXT_LINE_BREAK)) {
					int x;

					for (x = 0; x < bytes - 2; x++) {
						if (text_framedata[x] == 0xe2 && text_framedata[x+1] == 0x80 && text_framedata[x+2] == 0xa8) {
							text_framedata[x] = '\0';
							bytes = strlen((char *)text_framedata);
							break;
						}
					}
				}

				if (!bytes) continue;

				*(text_framedata + bytes) = '\r';
				*(text_framedata + bytes + 1) = '\n';
				*(text_framedata + bytes + 2) = '\0';

				frame.data = text_framedata;
				frame.datalen = strlen((char *)frame.data);
				read_frame = &frame;

			} else {
				continue;
			}

		}

		if (!switch_test_flag(read_frame, SFF_CNG)) {
			switch_status_t tstatus = switch_core_session_write_text_frame(vh->session_b, read_frame, 0, 0);

			if (tstatus != SWITCH_STATUS_SUCCESS) {
				switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
			}
		}
		switch_core_session_write_text_frame(vh->session_a, NULL, 0, 0);
	}

	vh->up = 0;

	switch_buffer_destroy(&text_buffer);
	switch_safe_free(text_framedata);
}


#ifdef SWITCH_VIDEO_IN_THREADS

static void video_bridge_thread(switch_core_session_t *session, void *obj)
{
	struct vid_helper *vh = obj;
	switch_channel_t *channel = switch_core_session_get_channel(vh->session_a);
	switch_channel_t *b_channel = switch_core_session_get_channel(vh->session_b);
	switch_status_t status;
	switch_frame_t *read_frame = 0;
	int set_decoded_read = 0, refresh_timer = 0;
	int refresh_cnt = 300;
	int pass_val = 0, last_pass_val = 0;

	vh->up = 1;

	if (switch_core_session_read_lock(vh->session_a) != SWITCH_STATUS_SUCCESS) {
		vh->up = 0;
		return;
	}

	if (switch_core_session_read_lock(vh->session_b) != SWITCH_STATUS_SUCCESS) {
		vh->up = 0;
		switch_core_session_rwunlock(vh->session_a);
		return;
	}

	switch_core_session_request_video_refresh(vh->session_a);
	switch_core_session_request_video_refresh(vh->session_b);

	refresh_timer = refresh_cnt;

	while (switch_channel_up_nosig(channel) && switch_channel_up_nosig(b_channel) && vh->up == 1) {
		if (switch_channel_media_up(channel)) {
			if (switch_core_session_transcoding(vh->session_a, vh->session_b, SWITCH_MEDIA_TYPE_VIDEO)) {
				pass_val = 1;
			} else {
				pass_val = 2;
			}

			if (pass_val != last_pass_val) {
				switch_core_session_passthru(session, SWITCH_MEDIA_TYPE_VIDEO, pass_val == 2 ? SWITCH_TRUE : SWITCH_FALSE);
				last_pass_val = pass_val;
			}
			
			if (switch_channel_test_flag(channel, CF_VIDEO_REFRESH_REQ)) {
				switch_channel_clear_flag(channel, CF_VIDEO_REFRESH_REQ);
				refresh_timer = refresh_cnt;
			}

			if (!switch_channel_test_flag(channel, CF_PROXY_MEDIA)) {
				switch_codec_implementation_t session_a_codec_implementation;
				switch_codec_implementation_t session_b_codec_implementation;

				switch_core_session_get_video_read_impl(vh->session_a, &session_a_codec_implementation);
				switch_core_session_get_video_write_impl(vh->session_b, &session_b_codec_implementation);

				if (switch_channel_test_flag(channel, CF_VIDEO_DECODED_READ)) {
					if (session_a_codec_implementation.impl_id == session_b_codec_implementation.impl_id && !switch_channel_test_flag(b_channel, CF_VIDEO_DECODED_READ)) {
						if (set_decoded_read) {
							switch_channel_clear_flag_recursive(channel, CF_VIDEO_DECODED_READ);
							set_decoded_read = 0;
							refresh_timer = refresh_cnt;
						}
					}
				} else {
					if (session_a_codec_implementation.impl_id != session_b_codec_implementation.impl_id ||
						switch_channel_test_flag(b_channel, CF_VIDEO_DECODED_READ)) {
						switch_channel_set_flag_recursive(channel, CF_VIDEO_DECODED_READ);
						set_decoded_read = 1;
						refresh_timer = refresh_cnt;
					}
				}
			}

			if (refresh_timer) {
				if (refresh_timer > 0 && (refresh_timer % 100) == 0) {
					switch_core_session_request_video_refresh(vh->session_b);
				}
				refresh_timer--;
			}

			status = switch_core_session_read_video_frame(vh->session_a, &read_frame, SWITCH_IO_FLAG_NONE, 0);

			if (!SWITCH_READ_ACCEPTABLE(status)) {
				switch_cond_next();
				continue;
			}
		}


		if (read_frame && (switch_test_flag(read_frame, SFF_CNG) ||
			switch_channel_test_flag(channel, CF_LEG_HOLDING) || switch_channel_test_flag(b_channel, CF_VIDEO_READ_FILE_ATTACHED))) {
			continue;
		}

		if (read_frame && switch_channel_media_up(b_channel)) {
			if (switch_core_session_write_video_frame(vh->session_b, read_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) {
				switch_cond_next();
				continue;
			}
		}
	}

	if (set_decoded_read) {
		switch_channel_clear_flag_recursive(channel, CF_VIDEO_DECODED_READ);
	}

	switch_core_session_kill_channel(vh->session_b, SWITCH_SIG_BREAK);
	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(vh->session_a), SWITCH_LOG_DEBUG, "%s video thread ended.\n", switch_channel_get_name(channel));

	switch_core_session_request_video_refresh(vh->session_a);
	switch_core_session_request_video_refresh(vh->session_b);

	switch_core_session_passthru(vh->session_a, SWITCH_MEDIA_TYPE_VIDEO, SWITCH_FALSE);

	switch_core_session_rwunlock(vh->session_a);
	switch_core_session_rwunlock(vh->session_b);

	vh->up = 0;
	return;
}

static void launch_video(struct vid_helper *vh)
{
	switch_core_media_start_engine_function(vh->session_a, SWITCH_MEDIA_TYPE_VIDEO, video_bridge_thread, vh);
}
#endif


static void send_display(switch_core_session_t *session, switch_core_session_t *peer_session)
{

	switch_core_session_message_t *msg;
	switch_caller_profile_t *caller_profile, *peer_caller_profile;
	switch_channel_t *caller_channel, *peer_channel;
	const char *name, *number, *p;

	caller_channel = switch_core_session_get_channel(session);
	peer_channel = switch_core_session_get_channel(peer_session);

	caller_profile = switch_channel_get_caller_profile(caller_channel);
	peer_caller_profile = switch_channel_get_caller_profile(peer_channel);

	if (switch_channel_test_flag(caller_channel, CF_BRIDGE_ORIGINATOR)) {
		if (!zstr(peer_caller_profile->caller_id_name)) {
			name = peer_caller_profile->caller_id_name;
		} else {
			name = caller_profile->caller_id_name;
		}

		if (!zstr(peer_caller_profile->caller_id_number)) {
			number = peer_caller_profile->caller_id_number;
		} else {
			number = caller_profile->caller_id_number;
		}

		if (zstr(number)) {
			number = "UNKNOWN";
		}
	} else {
		name = caller_profile->callee_id_name;
		number = caller_profile->callee_id_number;

		if (zstr(number)) {
			number = caller_profile->destination_number;
		}
	}


	if (zstr(name)) {
		name = number;
	}

	if ((p = strrchr(number, '/'))) {
		number = p + 1;
	}
	if ((p = strrchr(name, '/'))) {
		name = p + 1;
	}

	msg = switch_core_session_alloc(peer_session, sizeof(*msg));
	MESSAGE_STAMP_FFL(msg);
	msg->message_id = SWITCH_MESSAGE_INDICATE_DISPLAY;
	msg->string_array_arg[0] = switch_core_session_strdup(peer_session, name);
	msg->string_array_arg[1] = switch_core_session_strdup(peer_session, number);
	msg->from = __FILE__;
	switch_core_session_queue_message(peer_session, msg);

}


SWITCH_DECLARE(void) switch_ivr_bridge_display(switch_core_session_t *session, switch_core_session_t *peer_session)
{

	send_display(session, peer_session);
	send_display(peer_session, session);

}

struct switch_ivr_bridge_data {
	switch_core_session_t *session;
	char b_uuid[SWITCH_UUID_FORMATTED_LENGTH + 1];
	int stream_id;
	switch_input_callback_function_t input_callback;
	void *session_data;
	int clean_exit;
	int done;
	struct switch_ivr_bridge_data *other_leg_data;
};
typedef struct switch_ivr_bridge_data switch_ivr_bridge_data_t;

static void *audio_bridge_thread(switch_thread_t *thread, void *obj)
{
	switch_ivr_bridge_data_t *data = obj;
	int stream_id = 0, pre_b = 0, ans_a = 0, ans_b = 0, originator = 0;
	switch_input_callback_function_t input_callback;
	switch_core_session_message_t msg = { 0 };
	void *user_data;
	switch_channel_t *chan_a, *chan_b;
	switch_frame_t *read_frame;
	switch_core_session_t *session_a, *session_b;
	uint32_t read_frame_count = 0;
	const char *app_name = NULL, *app_arg = NULL;
	int inner_bridge = 0, exec_check = 0;
	switch_codec_t silence_codec = { 0 };
	switch_frame_t silence_frame = { 0 };
	int16_t silence_data[SWITCH_RECOMMENDED_BUFFER_SIZE / 2] = { 0 };
	const char *silence_var;
	int silence_val = 0, bypass_media_after_bridge = 0;
	const char *bridge_answer_timeout = NULL;
	int bridge_filter_dtmf, answer_timeout, sent_update = 0;
	time_t answer_limit = 0;
	const char *exec_app = NULL;
	const char *exec_data = NULL;
	switch_codec_implementation_t read_impl = { 0 };
	uint32_t txt_launch = 0;
	struct vid_helper th = { 0 };
	const char *banner_file = NULL;
	int played_banner = 0, banner_counter = 0;
	int pass_val = 0, last_pass_val = 0;

#ifdef SWITCH_VIDEO_IN_THREADS
	struct vid_helper vh = { 0 };
	uint32_t vid_launch = 0;
#endif
	data->clean_exit = 0;

	session_a = data->session;
	if (!(session_b = switch_core_session_locate(data->b_uuid))) {
		return NULL;
	}

	switch_core_session_get_read_impl(session_a, &read_impl);

	input_callback = data->input_callback;
	user_data = data->session_data;
	stream_id = data->stream_id;

	chan_a = switch_core_session_get_channel(session_a);
	chan_b = switch_core_session_get_channel(session_b);

	if ((exec_app = switch_channel_get_variable(chan_a, "bridge_pre_execute_app"))) {
		exec_data = switch_channel_get_variable(chan_a, "bridge_pre_execute_data");
	}

	bypass_media_after_bridge = switch_channel_test_flag(chan_a, CF_BYPASS_MEDIA_AFTER_BRIDGE);
	switch_channel_clear_flag(chan_a, CF_BYPASS_MEDIA_AFTER_BRIDGE);

	ans_a = switch_channel_test_flag(chan_a, CF_ANSWERED);

	if ((originator = switch_channel_test_flag(chan_a, CF_BRIDGE_ORIGINATOR))) {
		pre_b = switch_channel_test_flag(chan_a, CF_EARLY_MEDIA);
		ans_b = switch_channel_test_flag(chan_b, CF_ANSWERED);
	}

	inner_bridge = switch_channel_test_flag(chan_a, CF_INNER_BRIDGE);

	if (!switch_channel_test_flag(chan_a, CF_ANSWERED) && (bridge_answer_timeout = switch_channel_get_variable(chan_a, "bridge_answer_timeout"))) {
		if ((answer_timeout = atoi(bridge_answer_timeout)) >= 0) {
			answer_limit = switch_epoch_time_now(NULL) + answer_timeout;
		}
	}

	switch_channel_clear_flag(chan_a, CF_INTERCEPT);
	switch_channel_clear_flag(chan_a, CF_INTERCEPTED);

	switch_channel_set_flag(chan_a, CF_BRIDGED);

	switch_channel_wait_for_flag(chan_b, CF_BRIDGED, SWITCH_TRUE, 10000, chan_a);

	if (!switch_channel_test_flag(chan_b, CF_BRIDGED)) {
		if (!(switch_channel_test_flag(chan_b, CF_TRANSFER) || switch_channel_test_flag(chan_b, CF_REDIRECT)
			  || switch_channel_get_state(chan_b) == CS_RESET)) {
			switch_channel_hangup(chan_b, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
		}
		goto end_of_bridge_loop;
	}


	if (switch_channel_test_flag(chan_a, CF_BRIDGE_ORIGINATOR) && (banner_file = switch_channel_get_variable(chan_a, "video_pre_call_banner"))) {
		switch_channel_set_flag(chan_a, CF_AUDIO_PAUSE_READ);
		switch_channel_set_flag(chan_b, CF_AUDIO_PAUSE_READ);
		switch_channel_set_flag(chan_a, CF_AUDIO_PAUSE_WRITE);
		switch_channel_set_flag(chan_b, CF_AUDIO_PAUSE_WRITE);
		switch_channel_set_flag(chan_a, CF_VIDEO_PAUSE_READ);
		switch_channel_set_flag(chan_b, CF_VIDEO_PAUSE_READ);
	}


	if (bypass_media_after_bridge) {
		const char *source_a = switch_channel_get_variable(chan_a, "source");
		const char *source_b = switch_channel_get_variable(chan_b, "source");

		if (!source_a) source_a = "";
		if (!source_b) source_b = "";

		if (switch_stristr("loopback", source_a) || switch_stristr("loopback", source_b)) {
			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_WARNING, "Cannot bypass media while bridged to a loopback address.\n");
			bypass_media_after_bridge = 0;
		}
	}

	if ((silence_var = switch_channel_get_variable(chan_a, "bridge_generate_comfort_noise"))) {

		if (!switch_channel_media_up(chan_a)) {
			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_ERROR, "Channel has no media!\n");
			goto end_of_bridge_loop;
		}

		if (switch_true(silence_var)) {
			silence_val = 1400;
		} else {
			if ((silence_val = atoi(silence_var)) < -1) {
				silence_val = 0;
			}
		}

		if (silence_val) {
			if (switch_core_codec_init(&silence_codec,
									   "L16",
									   NULL,
									   NULL,
									   read_impl.actual_samples_per_second,
									   read_impl.microseconds_per_packet / 1000,
									   1,
									   SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
									   NULL, switch_core_session_get_pool(session_a)) != SWITCH_STATUS_SUCCESS) {

				silence_val = 0;
			} else {
				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "Setup generated silence from %s to %s at %d\n", switch_channel_get_name(chan_a),
								  switch_channel_get_name(chan_b), silence_val);
				silence_frame.codec = &silence_codec;
				silence_frame.data = silence_data;
				silence_frame.buflen = sizeof(silence_data);
				silence_frame.datalen = read_impl.decoded_bytes_per_packet;
				silence_frame.samples = silence_frame.datalen / sizeof(int16_t);
			}
		}
	}

	bridge_filter_dtmf = switch_true(switch_channel_get_variable(chan_a, "bridge_filter_dtmf"));


	for (;;) {
		switch_status_t status;
		switch_event_t *event;

		if (switch_core_session_transcoding(session_a, session_b, SWITCH_MEDIA_TYPE_AUDIO)) {
			pass_val = 1;
		} else {
			pass_val = 2;
		}
		
		if (pass_val != last_pass_val) {
			switch_core_session_passthru(session_a, SWITCH_MEDIA_TYPE_AUDIO, pass_val == 2 ? SWITCH_TRUE : SWITCH_FALSE);
			last_pass_val = pass_val;
		}
		
		if (switch_channel_test_flag(chan_a, CF_TRANSFER)) {
			data->clean_exit = 1;
		}

		if (data->clean_exit || switch_channel_test_flag(chan_b, CF_TRANSFER)) {
			switch_channel_clear_flag(chan_a, CF_HOLD);
			switch_channel_clear_flag(chan_a, CF_SUSPEND);
			goto end_of_bridge_loop;
		}

		if (!switch_channel_test_flag(chan_b, CF_BRIDGED)) {
			goto end_of_bridge_loop;
		}

		if (!switch_channel_ready(chan_a)) {
			if (switch_channel_up(chan_a)) {
				data->clean_exit = 1;
			}
			goto end_of_bridge_loop;
		}

		if (switch_channel_down_nosig(chan_b)) {
			goto end_of_bridge_loop;
		}

		if (switch_channel_test_flag(chan_a, CF_HOLD_ON_BRIDGE)) {
			switch_core_session_message_t hmsg = { 0 };
			switch_channel_clear_flag(chan_a, CF_HOLD_ON_BRIDGE);
			hmsg.message_id = SWITCH_MESSAGE_INDICATE_HOLD;
			hmsg.from = __FILE__;
			hmsg.numeric_arg = 1;
			switch_core_session_receive_message(session_a, &hmsg);
		}

		if (read_frame_count > DEFAULT_LEAD_FRAMES && switch_channel_media_ack(chan_a) && switch_core_session_private_event_count(session_a)) {
			switch_channel_set_flag(chan_b, CF_SUSPEND);
			msg.numeric_arg = 42;
			msg.string_arg = data->b_uuid;
			msg.message_id = SWITCH_MESSAGE_INDICATE_UNBRIDGE;
			msg.from = __FILE__;
			switch_core_session_receive_message(session_a, &msg);
			switch_ivr_parse_next_event(session_a);
			msg.message_id = SWITCH_MESSAGE_INDICATE_BRIDGE;
			switch_core_session_receive_message(session_a, &msg);
			switch_channel_clear_flag(chan_b, CF_SUSPEND);
			switch_core_session_kill_channel(session_b, SWITCH_SIG_BREAK);
		}

		switch_ivr_parse_all_messages(session_a);

		if (!inner_bridge && (switch_channel_test_flag(chan_a, CF_SUSPEND) || switch_channel_test_flag(chan_b, CF_SUSPEND))) {
			status = switch_core_session_read_frame(session_a, &read_frame, SWITCH_IO_FLAG_NONE, stream_id);

			if (!SWITCH_READ_ACCEPTABLE(status)) {
				goto end_of_bridge_loop;
			}
			continue;
		}

		if (switch_channel_test_flag(chan_a, CF_HAS_TEXT) && switch_channel_test_flag(chan_b, CF_HAS_TEXT) && !txt_launch) {
			txt_launch++;

			th.session_a = session_a;
			th.session_b = session_b;
			switch_core_media_start_engine_function(th.session_a, SWITCH_MEDIA_TYPE_TEXT, text_bridge_thread, &th);

		}

#ifdef SWITCH_VIDEO_IN_THREADS
		if (switch_channel_test_flag(chan_a, CF_VIDEO) && switch_channel_test_flag(chan_b, CF_VIDEO) && !vid_launch) {
			vid_launch++;
			vh.session_a = session_a;
			vh.session_b = session_b;
			switch_channel_clear_flag(chan_a, CF_VIDEO_BLANK);
			switch_channel_clear_flag(chan_b, CF_VIDEO_BLANK);
			launch_video(&vh);
		} else {
			if (switch_channel_test_flag(chan_a, CF_VIDEO)) {
				switch_channel_set_flag(chan_a, CF_VIDEO_BLANK);
			}

			if (switch_channel_test_flag(chan_b, CF_VIDEO)) {
				switch_channel_set_flag(chan_b, CF_VIDEO_BLANK);
			}
		}
#endif

		if (read_frame_count >= DEFAULT_LEAD_FRAMES && switch_channel_media_ack(chan_a)) {
			if (!played_banner && switch_channel_test_flag(chan_a, CF_VIDEO) && switch_channel_test_flag(chan_b, CF_VIDEO) &&
				switch_channel_test_flag(chan_a, CF_ANSWERED) && switch_channel_test_flag(chan_b, CF_ANSWERED) &&
				++banner_counter > 100 &&
				switch_channel_test_flag(chan_a, CF_VIDEO_READY) && switch_channel_test_flag(chan_b, CF_VIDEO_READY) &&
				switch_channel_test_flag(chan_a, CF_BRIDGE_ORIGINATOR) && banner_file) {
				const char *b_banner_file = switch_channel_get_variable(chan_b, "video_pre_call_banner");

				if (!b_banner_file) {
					b_banner_file = switch_channel_get_variable(chan_a, "video_pre_call_banner_bleg");
				}


				switch_channel_clear_flag(chan_a, CF_VIDEO_PAUSE_READ);
				switch_channel_clear_flag(chan_b, CF_VIDEO_PAUSE_READ);

				if (b_banner_file) {
					switch_ivr_broadcast(switch_core_session_get_uuid(session_a), banner_file, SMF_ECHO_ALEG);
					switch_ivr_broadcast(switch_core_session_get_uuid(session_b), b_banner_file, SMF_ECHO_ALEG);
				} else {
					switch_ivr_broadcast(switch_core_session_get_uuid(session_a), banner_file, SMF_ECHO_ALEG | SMF_ECHO_BLEG);
				}

				played_banner = 1;

				switch_channel_clear_flag(chan_a, CF_AUDIO_PAUSE_READ);
				switch_channel_clear_flag(chan_b, CF_AUDIO_PAUSE_READ);
				switch_channel_clear_flag(chan_a, CF_AUDIO_PAUSE_WRITE);
				switch_channel_clear_flag(chan_b, CF_AUDIO_PAUSE_WRITE);
			}



			if (!exec_check) {
				switch_channel_execute_on(chan_a, SWITCH_CHANNEL_EXECUTE_ON_PRE_BRIDGE_VARIABLE);

				if (!inner_bridge) {
					switch_channel_api_on(chan_a, SWITCH_API_BRIDGE_START_VARIABLE);
				}
				exec_check = 1;
			}

			if (exec_app) {
				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "%s Bridge execute app %s(%s)\n",
								  switch_channel_get_name(chan_a), exec_app, exec_data);

				switch_core_session_execute_application_async(session_a, exec_app, exec_data);
				exec_app = exec_data = NULL;
			}

			if ((bypass_media_after_bridge || switch_channel_test_flag(chan_b, CF_BYPASS_MEDIA_AFTER_BRIDGE)) && switch_channel_test_flag(chan_a, CF_ANSWERED)
				&& switch_channel_test_flag(chan_b, CF_ANSWERED)) {

				if (switch_true(switch_channel_get_variable_dup(chan_a, "bypass_media_after_bridge_oldschool", SWITCH_FALSE, -1))) {
					switch_ivr_nomedia(switch_core_session_get_uuid(session_a), SMF_REBRIDGE);
				} else {
					switch_ivr_3p_nomedia(switch_core_session_get_uuid(session_a), SMF_REBRIDGE);
				}
				switch_channel_clear_flag(chan_b, CF_BYPASS_MEDIA_AFTER_BRIDGE);
				goto end_of_bridge_loop;
			}
		}

		/* if 1 channel has DTMF pass it to the other */
		while (switch_channel_has_dtmf(chan_a)) {
			switch_dtmf_t dtmf = { 0, 0 };
			if (switch_channel_dequeue_dtmf(chan_a, &dtmf) == SWITCH_STATUS_SUCCESS) {
				int send_dtmf = 1;

				if (input_callback) {
					switch_status_t cb_status = input_callback(session_a, (void *) &dtmf, SWITCH_INPUT_TYPE_DTMF, user_data, 0);

					if (cb_status == SWITCH_STATUS_IGNORE) {
						send_dtmf = 0;
					} else if (cb_status != SWITCH_STATUS_SUCCESS) {
						switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "%s ended call via DTMF\n", switch_channel_get_name(chan_a));
						switch_core_session_kill_channel(session_b, SWITCH_SIG_BREAK);
						goto end_of_bridge_loop;
					}
				}

				if (bridge_filter_dtmf) {
					send_dtmf = 0;
					switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "Dropping filtered DTMF received on %s\n", switch_channel_get_name(chan_a));
				}

				if (send_dtmf) {
					switch_core_session_send_dtmf(session_b, &dtmf);
					switch_core_session_kill_channel(session_b, SWITCH_SIG_BREAK);
				}
			}
		}

		if (switch_core_session_dequeue_event(session_a, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
			if (input_callback) {
				input_callback(session_a, event, SWITCH_INPUT_TYPE_EVENT, user_data, 0);
			}

			if ((event->event_id != SWITCH_EVENT_COMMAND && event->event_id != SWITCH_EVENT_MESSAGE)
				|| switch_core_session_receive_event(session_b, &event) != SWITCH_STATUS_SUCCESS) {
				switch_event_destroy(&event);
			}

		}

		if (!switch_channel_test_flag(chan_a, CF_ANSWERED) && answer_limit && switch_epoch_time_now(NULL) > answer_limit) {
			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "Answer timeout hit on %s.\n", switch_channel_get_name(chan_a));
			if (switch_true(switch_channel_get_variable_dup(chan_a, "continue_on_answer_timeout", SWITCH_FALSE, -1))) {
				data->clean_exit = 1;
				goto end_of_bridge_loop;
			} else {
				switch_channel_hangup(chan_a, SWITCH_CAUSE_ALLOTTED_TIMEOUT);
			}
		}

		if (!switch_channel_test_flag(chan_a, CF_ANSWERED)) {
			if (originator) {
				if (!ans_b && switch_channel_test_flag(chan_b, CF_ANSWERED)) {
					switch_channel_pass_callee_id(chan_b, chan_a);
					if (switch_channel_answer(chan_a) != SWITCH_STATUS_SUCCESS) {
						switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "%s Media Establishment Failed.\n", switch_channel_get_name(chan_a));
						goto end_of_bridge_loop;
					}
					ans_a = 1;
				} else if (!pre_b && switch_channel_test_flag(chan_b, CF_EARLY_MEDIA)) {
					if (switch_channel_pre_answer(chan_a) != SWITCH_STATUS_SUCCESS) {
						switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "%s Media Establishment Failed.\n", switch_channel_get_name(chan_a));
						goto end_of_bridge_loop;
					}
					pre_b = 1;
				}
				if (!pre_b) {
					switch_yield(10000);
					continue;
				}
			} else {
				ans_a = switch_channel_test_flag(chan_b, CF_ANSWERED);
			}
		}

		if (ans_a != ans_b) {
			switch_channel_t *un = ans_a ? chan_b : chan_a;
			switch_channel_t *a = un == chan_b ? chan_a : chan_b;

			if (switch_channel_direction(un) == SWITCH_CALL_DIRECTION_INBOUND) {
				if (switch_channel_direction(a) == SWITCH_CALL_DIRECTION_OUTBOUND || (un == chan_a && !originator)) {
					switch_channel_pass_callee_id(a, un);
				}

				if (switch_channel_answer(un) != SWITCH_STATUS_SUCCESS) {
					switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "%s Media Establishment Failed.\n", switch_channel_get_name(un));
					goto end_of_bridge_loop;
				}

				if (ans_a) {
					ans_b = 1;
				} else {
					ans_a = 1;
				}
			}
		}

		if (originator && !ans_b) ans_b = switch_channel_test_flag(chan_b, CF_ANSWERED);

		if (originator && !sent_update && ans_a && ans_b && switch_channel_media_ack(chan_a) && switch_channel_media_ack(chan_b)) {
			switch_ivr_bridge_display(session_a, session_b);
			sent_update = 1;
		}
#ifndef SWITCH_VIDEO_IN_THREADS
		if (switch_channel_test_flag(chan_a, CF_VIDEO) && switch_channel_test_flag(chan_b, CF_VIDEO)) {
			/* read video from 1 channel and write it to the other */
			status = switch_core_session_read_video_frame(session_a, &read_frame, SWITCH_IO_FLAG_NONE, 0);

			if (!SWITCH_READ_ACCEPTABLE(status)) {
				goto end_of_bridge_loop;
			}

			switch_core_session_write_video_frame(session_b, read_frame, SWITCH_IO_FLAG_NONE, 0);
		}
#endif

		if (!switch_channel_test_flag(chan_a, CF_AUDIO)) {
			switch_ivr_sleep(session_a, 5000, SWITCH_FALSE, NULL);
			continue;
		}


		/* read audio from 1 channel and write it to the other */
		status = switch_core_session_read_frame(session_a, &read_frame, SWITCH_IO_FLAG_NONE, stream_id);

		if (SWITCH_READ_ACCEPTABLE(status)) {
			read_frame_count++;
			if (switch_test_flag(read_frame, SFF_CNG)) {
				if (silence_val) {
					switch_generate_sln_silence((int16_t *) silence_frame.data, silence_frame.samples,
												read_impl.number_of_channels, silence_val);
					read_frame = &silence_frame;
				} else if (!switch_channel_test_flag(chan_b, CF_ACCEPT_CNG)) {
					continue;
				}
			}

			if (switch_channel_test_flag(chan_a, CF_BRIDGE_NOWRITE)) {
				continue;
			}

			if (status != SWITCH_STATUS_BREAK && !switch_channel_test_flag(chan_a, CF_HOLD) && !switch_channel_test_flag(chan_b, CF_LEG_HOLDING)) {
				if (switch_core_session_write_frame(session_b, read_frame, SWITCH_IO_FLAG_NONE, stream_id) != SWITCH_STATUS_SUCCESS) {
					switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG,
									  "%s ending bridge by request from write function\n", switch_channel_get_name(chan_b));
					goto end_of_bridge_loop;
				}
			}
		} else {
			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "%s ending bridge by request from read function\n", switch_channel_get_name(chan_a));
			goto end_of_bridge_loop;
		}
	}

  end_of_bridge_loop:

	switch_core_session_passthru(session_a, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_FALSE);


#ifdef SWITCH_VIDEO_IN_THREADS
	if (vh.up > 0) {
		vh.up = -1;
		switch_channel_set_flag(chan_a, CF_NOT_READY);
		//switch_channel_set_flag(chan_b, CF_NOT_READY);
		switch_core_session_kill_channel(session_a, SWITCH_SIG_BREAK);
		switch_core_session_kill_channel(session_b, SWITCH_SIG_BREAK);
		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "Ending video thread.\n");
	}
#endif


	if (silence_val) {
		switch_core_codec_destroy(&silence_codec);
	}

	switch_channel_execute_on(chan_a, SWITCH_CHANNEL_EXECUTE_ON_POST_BRIDGE_VARIABLE);

	if (!inner_bridge) {
		switch_channel_api_on(chan_a, SWITCH_API_BRIDGE_END_VARIABLE);
	}

	if (!inner_bridge && switch_channel_up_nosig(chan_a) && !switch_channel_test_flag(chan_a, CF_REDIRECT)) {
		if ((app_name = switch_channel_get_variable(chan_a, SWITCH_EXEC_AFTER_BRIDGE_APP_VARIABLE))) {
			switch_caller_extension_t *extension = NULL;
			if ((extension = switch_caller_extension_new(session_a, app_name, app_name)) == 0) {
				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_CRIT, "memory error!\n");
				goto end;
			}
			app_arg = switch_channel_get_variable(chan_a, SWITCH_EXEC_AFTER_BRIDGE_ARG_VARIABLE);

			switch_caller_extension_add_application(session_a, extension, (char *) app_name, app_arg);
			switch_channel_set_caller_extension(chan_a, extension);

			if (switch_channel_get_state(chan_a) == CS_EXECUTE) {
				switch_channel_set_flag(chan_a, CF_RESET);
			} else {
				switch_channel_set_state(chan_a, CS_EXECUTE);
			}
		}
	}

  end:


	if (switch_core_media_check_engine_function(session_a, SWITCH_MEDIA_TYPE_TEXT)) {
		if (th.up == 1) {
			th.up = -1;
		}

		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "Ending text thread.\n");
		switch_core_media_end_engine_function(session_a, SWITCH_MEDIA_TYPE_TEXT);
	}

#ifdef SWITCH_VIDEO_IN_THREADS
	if (switch_core_media_check_engine_function(session_a, SWITCH_MEDIA_TYPE_VIDEO)) {
		if (vh.up == 1) {
			vh.up = -1;
		}

		switch_channel_set_flag(chan_a, CF_VIDEO_BREAK);
		switch_channel_set_flag(chan_b, CF_VIDEO_BREAK);
		switch_core_session_kill_channel(session_a, SWITCH_SIG_BREAK);
		switch_core_session_kill_channel(session_b, SWITCH_SIG_BREAK);

		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "Ending video thread.\n");
		switch_core_media_end_engine_function(session_a, SWITCH_MEDIA_TYPE_VIDEO);
		switch_channel_clear_flag(chan_a, CF_NOT_READY);
		switch_channel_clear_flag(chan_b, CF_NOT_READY);
	}
#endif



	switch_core_session_reset(session_a, SWITCH_TRUE, SWITCH_TRUE);
	switch_channel_set_variable(chan_a, SWITCH_BRIDGE_VARIABLE, NULL);
	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "BRIDGE THREAD DONE [%s]\n", switch_channel_get_name(chan_a));
	switch_channel_clear_flag(chan_a, CF_BRIDGED);

	if (switch_channel_test_flag(chan_a, CF_LEG_HOLDING) || switch_channel_test_flag(chan_a, CF_HANGUP_HELD)) {
		if (switch_channel_ready(chan_b) &&
			switch_channel_get_state(chan_b) != CS_PARK && !data->other_leg_data->clean_exit && !switch_channel_test_flag(chan_b, CF_3P_NOMEDIA_REQUESTED)) {
			const char *ext = switch_channel_get_variable(chan_a, "hold_hangup_xfer_exten");

			switch_channel_stop_broadcast(chan_b);

			if (zstr(ext)) {
				switch_call_cause_t cause = switch_channel_get_cause(chan_b);
				if (cause == SWITCH_CAUSE_NONE) {
					cause = SWITCH_CAUSE_NORMAL_CLEARING;
				}
				switch_channel_hangup(chan_b, cause);
			} else {
				switch_channel_set_variable(chan_b, SWITCH_TRANSFER_AFTER_BRIDGE_VARIABLE, ext);
			}
		}

		if (switch_channel_test_flag(chan_a, CF_LEG_HOLDING)) {
			switch_channel_mark_hold(chan_a, SWITCH_FALSE);
		}
	}

	if (switch_channel_test_flag(chan_a, CF_INTERCEPTED)) {
		switch_channel_set_flag(chan_b, CF_INTERCEPT);
	}

	switch_channel_clear_flag(chan_a, CF_VIDEO_BLANK);
	switch_channel_clear_flag(chan_b, CF_VIDEO_BLANK);

	switch_core_session_kill_channel(session_b, SWITCH_SIG_BREAK);
	data->done = 1;
	switch_core_session_video_reset(session_a);
	switch_core_session_video_reset(session_b);

	switch_core_session_rwunlock(session_b);
	return NULL;
}

static void transfer_after_bridge(switch_core_session_t *session, const char *where)
{
	char *argv[4] = { 0 };
	char *mydata;

	switch_channel_set_variable(switch_core_session_get_channel(session), SWITCH_TRANSFER_AFTER_BRIDGE_VARIABLE, NULL);

	if (!zstr(where) && (mydata = switch_core_session_strdup(session, where))) {
		if (switch_separate_string(mydata, ':', argv, (sizeof(argv) / sizeof(argv[0]))) >= 1) {
			switch_ivr_session_transfer(session, argv[0], argv[1], argv[2]);
		} else {
			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No extension specified.\n");
		}
	}
}

static switch_status_t audio_bridge_on_exchange_media(switch_core_session_t *session)
{
	switch_channel_t *channel = switch_core_session_get_channel(session);
	switch_ivr_bridge_data_t *bd = switch_channel_get_private(channel, "_bridge_");
	switch_channel_state_t state;
	const char *var;

	if (bd) {
		switch_channel_set_private(channel, "_bridge_", NULL);
		if (bd->session == session && *bd->b_uuid) {
			audio_bridge_thread(NULL, (void *) bd);
			switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE);
		} else {
			switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
		}
	} else {
		switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
	}
	switch_channel_clear_state_handler(channel, &audio_bridge_peer_state_handlers);

	state = switch_channel_get_state(channel);

	if (!switch_channel_test_flag(channel, CF_TRANSFER) && !switch_channel_test_flag(channel, CF_REDIRECT) &&
		!switch_channel_test_flag(channel, CF_XFER_ZOMBIE) && bd && !bd->clean_exit && state != CS_PARK && state != CS_ROUTING &&
		state == CS_EXCHANGE_MEDIA && !switch_channel_test_flag(channel, CF_INNER_BRIDGE)) {

		if (state < CS_HANGUP && switch_true(switch_channel_get_variable(channel, SWITCH_PARK_AFTER_BRIDGE_VARIABLE))) {
			switch_ivr_park_session(session);
			return SWITCH_STATUS_FALSE;
		} else if (state < CS_HANGUP && (var = switch_channel_get_variable(channel, SWITCH_TRANSFER_AFTER_BRIDGE_VARIABLE))) {
			transfer_after_bridge(session, var);
			return SWITCH_STATUS_FALSE;
		}

		if (switch_channel_test_flag(channel, CF_INTERCEPTED)) {
			switch_channel_clear_flag(channel, CF_INTERCEPT);
			switch_channel_clear_flag(channel, CF_INTERCEPTED);
			return SWITCH_STATUS_FALSE;
		} else {
			if (switch_channel_test_flag(channel, CF_INTERCEPT)) {
				switch_channel_hangup(channel, SWITCH_CAUSE_PICKED_OFF);
			} else {
				if (!switch_channel_test_flag(channel, CF_ANSWERED)) {
					int x = 0;

					if (switch_channel_execute_on(channel, "execute_on_orphaned_bleg") == SWITCH_STATUS_SUCCESS) {
						x++;
					}

					if (switch_channel_api_on(channel, "api_on_orphaned_bleg") == SWITCH_STATUS_SUCCESS) {
						x++;
					}

					if (!x) {
						switch_channel_hangup(channel, SWITCH_CAUSE_ORIGINATOR_CANCEL);
					}
					
				} else {
					switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
				}
			}
		}
	}

	if (switch_channel_get_state(channel) == CS_EXCHANGE_MEDIA) {
		switch_channel_set_variable(channel, "park_timeout", "3");
		switch_channel_set_state(channel, CS_PARK);
	}

	return SWITCH_STATUS_FALSE;
}

static switch_status_t audio_bridge_on_routing(switch_core_session_t *session)
{
	switch_channel_t *channel = switch_core_session_get_channel(session);

	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CUSTOM ROUTING\n", switch_channel_get_name(channel));

	/* put the channel in a passive state so we can loop audio to it */
	switch_channel_set_state(channel, CS_CONSUME_MEDIA);
	return SWITCH_STATUS_FALSE;
}

static switch_status_t audio_bridge_on_consume_media(switch_core_session_t *session)
{
	switch_channel_t *channel = switch_core_session_get_channel(session);

	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CUSTOM HOLD\n", switch_channel_get_name(channel));

	/* put the channel in a passive state so we can loop audio to it */
	return SWITCH_STATUS_FALSE;
}

static const switch_state_handler_table_t audio_bridge_peer_state_handlers = {
	/*.on_init */ NULL,
	/*.on_routing */ audio_bridge_on_routing,
	/*.on_execute */ NULL,
	/*.on_hangup */ NULL,
	/*.on_exchange_media */ audio_bridge_on_exchange_media,
	/*.on_soft_execute */ NULL,
	/*.on_consume_media */ audio_bridge_on_consume_media,
};


SWITCH_DECLARE(switch_status_t) switch_ivr_bridge_bleg(switch_core_session_t *session, switch_core_session_t *peer_session, uint32_t max_wait_ms)
{
	switch_channel_t *channel;
	switch_channel_t *peer_channel = NULL;
	switch_status_t status = SWITCH_STATUS_SUCCESS;

	switch_assert(session);
	channel = switch_core_session_get_channel(session);
	
	switch_channel_set_flag(channel, CF_ARRANGED_BRIDGE);

	if (peer_session) {
		peer_channel = switch_core_session_get_channel(peer_session);
	}

	status = switch_channel_wait_for_flag(channel, CF_ARRANGED_BRIDGE, SWITCH_FALSE, max_wait_ms, peer_channel);

	if (status == SWITCH_STATUS_FALSE) return status;

	if (switch_channel_test_flag(channel, CF_ARRANGED_BRIDGE)) {
		switch_channel_clear_flag(channel, CF_ARRANGED_BRIDGE);
		return SWITCH_STATUS_FALSE;
	} else {
		switch_channel_t *channel = switch_core_session_get_channel(session);
		if (!switch_channel_test_flag(channel, CF_TRANSFER)) {
			audio_bridge_on_exchange_media(session);
		}
	}

	return SWITCH_STATUS_SUCCESS;
}



static switch_status_t uuid_bridge_on_reset(switch_core_session_t *session);
static switch_status_t uuid_bridge_on_hibernate(switch_core_session_t *session);
static switch_status_t uuid_bridge_on_soft_execute(switch_core_session_t *session);

static const switch_state_handler_table_t uuid_bridge_state_handlers = {
	/*.on_init */ NULL,
	/*.on_routing */ NULL,
	/*.on_execute */ NULL,
	/*.on_hangup */ NULL,
	/*.on_exchange_media */ NULL,
	/*.on_soft_execute */ uuid_bridge_on_soft_execute,
	/*.on_consume_media */ uuid_bridge_on_hibernate,
	/*.on_hibernate */ uuid_bridge_on_hibernate,
	/*.on_reset */ uuid_bridge_on_reset
};

static switch_status_t uuid_bridge_on_reset(switch_core_session_t *session)
{
	switch_channel_t *channel = switch_core_session_get_channel(session);

	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CUSTOM RESET\n", switch_channel_get_name(channel));

	switch_channel_clear_flag(channel, CF_ORIGINATING);

	cleanup_proxy_mode_b(session);

	if (switch_channel_test_flag(channel, CF_UUID_BRIDGE_ORIGINATOR)) {
		switch_channel_set_state(channel, CS_SOFT_EXECUTE);
	}

	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t uuid_bridge_on_hibernate(switch_core_session_t *session)
{
	switch_channel_set_state(switch_core_session_get_channel(session), CS_RESET);
	return SWITCH_STATUS_FALSE;
}

static switch_status_t uuid_bridge_on_soft_execute(switch_core_session_t *session)
{
	switch_channel_t *channel = switch_core_session_get_channel(session);
	switch_core_session_t *other_session = NULL;
	const char *other_uuid = NULL;

	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CUSTOM SOFT_EXECUTE\n", switch_channel_get_name(channel));
	switch_channel_clear_state_handler(channel, &uuid_bridge_state_handlers);

	if (!switch_channel_test_flag(channel, CF_UUID_BRIDGE_ORIGINATOR)) {
		return SWITCH_STATUS_SUCCESS;
	}

	if ((other_uuid = switch_channel_get_variable(channel, SWITCH_UUID_BRIDGE)) && (other_session = switch_core_session_locate(other_uuid))) {
		switch_channel_t *other_channel = switch_core_session_get_channel(other_session);
		switch_event_t *event;
		int ready_a, ready_b;
		switch_channel_state_t state, running_state;
		int max = 1000, loops = max;

		switch_channel_set_variable(channel, SWITCH_UUID_BRIDGE, NULL);

		for (;;) {
			state = switch_channel_get_state(other_channel);
			running_state = switch_channel_get_running_state(other_channel);

			if (switch_channel_down_nosig(other_channel) || switch_channel_down(channel)) {
				break;
			}

			if (state < CS_HANGUP && state == running_state) {

				if (--loops < 1) {
					switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
					switch_channel_hangup(other_channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
				}

				if (running_state == CS_RESET) {
					switch_channel_set_state(other_channel, CS_SOFT_EXECUTE);
				}

				if (running_state == CS_SOFT_EXECUTE) {

					if (switch_channel_test_flag(other_channel, CF_UUID_BRIDGE_ORIGINATOR)) {
						goto done;
					} else {
						break;
					}
				}

			} else {
				loops = max;
			}

			switch_yield(20000);
		}

		switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE);

		if (switch_ivr_wait_for_answer(session, other_session) != SWITCH_STATUS_SUCCESS) {
			if (switch_true(switch_channel_get_variable(channel, "uuid_bridge_continue_on_cancel"))) {
				switch_channel_set_state(channel, CS_EXECUTE);
			} else if (switch_true(switch_channel_get_variable(channel, "uuid_bridge_park_on_cancel"))) {
				switch_ivr_park_session(session);
			} else if (!switch_channel_test_flag(channel, CF_TRANSFER)) {
				switch_channel_hangup(channel, SWITCH_CAUSE_ORIGINATOR_CANCEL);
			}
			goto done;
		}

		ready_a = switch_channel_ready(channel);
		ready_b = switch_channel_ready(other_channel);

		if (!ready_a || !ready_b) {
			if (!ready_a) {
				switch_channel_hangup(other_channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
			}

			if (!ready_b) {
				const char *cid = switch_channel_get_variable(other_channel, "rdnis");
				if (ready_a && cid) {
					switch_ivr_session_transfer(session, cid, NULL, NULL);
				} else {
					switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
				}
			}
			goto done;
		}

		/* fire events that will change the data table from "show channels" */
		if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_EXECUTE) == SWITCH_STATUS_SUCCESS) {
			switch_channel_event_set_data(channel, event);
			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Application", "uuid_bridge");
			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Application-Data", switch_core_session_get_uuid(other_session));
			switch_event_fire(&event);
		}

		if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_EXECUTE) == SWITCH_STATUS_SUCCESS) {
			switch_channel_event_set_data(other_channel, event);
			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Application", "uuid_bridge");
			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Application-Data", switch_core_session_get_uuid(session));
			switch_event_fire(&event);
		}

		switch_ivr_multi_threaded_bridge(session, other_session, NULL, NULL, NULL);

		state = switch_channel_get_state(channel);
		if (!switch_channel_test_flag(channel, CF_TRANSFER) &&
			!switch_channel_test_flag(channel, CF_REDIRECT) && state < CS_HANGUP && state != CS_ROUTING && state != CS_PARK) {
			switch_channel_set_state(channel, CS_EXECUTE);
		}
	} else {
		int hup = 1;
		const char *var;
		
		if (switch_true(switch_channel_get_variable(channel, SWITCH_PARK_AFTER_BRIDGE_VARIABLE))) {
			switch_ivr_park_session(session);
			hup = 0;
		} else if ((var = switch_channel_get_variable(channel, SWITCH_TRANSFER_AFTER_BRIDGE_VARIABLE))) {
			transfer_after_bridge(session, var);
			hup = 0;
		}
		
		if (hup) {
			switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
		}
	}

 done:

	if (other_session) {
		switch_core_session_rwunlock(other_session);
		other_session = NULL;
	}

	switch_channel_clear_flag(channel, CF_UUID_BRIDGE_ORIGINATOR);

	return SWITCH_STATUS_FALSE;
}

static switch_status_t sb_on_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf, switch_dtmf_direction_t direction)
{
	switch_channel_t *channel = NULL;
	char *key;

	channel = switch_core_session_get_channel(session);
	switch_assert(channel != NULL);

	if ((key = (char *) switch_channel_get_private(channel, "__bridge_term_key")) && dtmf->digit == *key) {
		const char *uuid;
		switch_core_session_t *other_session;

		if (switch_channel_test_flag(channel, CF_BRIDGE_ORIGINATOR)) {
			switch_channel_set_state(channel, CS_EXECUTE);
		} else {
			if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE)) && (other_session = switch_core_session_locate(uuid))) {
				switch_channel_t *other_channel = switch_core_session_get_channel(other_session);
				switch_channel_set_state(other_channel, CS_EXECUTE);
				switch_core_session_rwunlock(other_session);
			} else {
				return SWITCH_STATUS_SUCCESS;
			}
		}

		return SWITCH_STATUS_FALSE;
	}

	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t hanguphook(switch_core_session_t *session)
{
	switch_core_session_message_t *msg = NULL;
	switch_channel_t *channel = NULL;
	switch_event_t *event;

	channel = switch_core_session_get_channel(session);

	if (switch_channel_test_flag(channel, CF_BRIDGE_ORIGINATOR)) {
		switch_channel_clear_flag_recursive(channel, CF_BRIDGE_ORIGINATOR);
		if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_UNBRIDGE) == SWITCH_STATUS_SUCCESS) {
			switch_channel_event_set_data(channel, event);
			switch_event_fire(&event);
		}
	}


	msg = switch_core_session_alloc(session, sizeof(*msg));
	MESSAGE_STAMP_FFL(msg);
	msg->message_id = SWITCH_MESSAGE_INDICATE_UNBRIDGE;
	msg->from = __FILE__;
	msg->string_arg = switch_channel_get_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE);
	switch_core_session_queue_message(session, msg);

	switch_core_event_hook_remove_state_change(session, hanguphook);

	return SWITCH_STATUS_SUCCESS;

}

static switch_status_t signal_bridge_on_hibernate(switch_core_session_t *session)
{
	switch_channel_t *channel = NULL;
	const char *key;
	switch_core_session_message_t msg = { 0 };
	switch_event_t *event = NULL;
	switch_ivr_dmachine_t *dmachine[2] = { 0 };

	channel = switch_core_session_get_channel(session);
	switch_assert(channel != NULL);

	msg.message_id = SWITCH_MESSAGE_INDICATE_BRIDGE;
	msg.from = __FILE__;
	msg.string_arg = switch_channel_get_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE);

	switch_core_event_hook_add_state_change(session, hanguphook);

	switch_core_session_receive_message(session, &msg);

	if ((key = switch_channel_get_variable(channel, "bridge_terminate_key"))) {
		switch_channel_set_private(channel, "__bridge_term_key", switch_core_session_strdup(session, key));
		switch_core_event_hook_add_recv_dtmf(session, sb_on_dtmf);
	}

	switch_channel_set_variable(channel, SWITCH_BRIDGE_VARIABLE, switch_channel_get_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE));
	switch_channel_set_variable(channel, SWITCH_LAST_BRIDGE_VARIABLE, switch_channel_get_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE));

	switch_channel_set_bridge_time(channel);


	if (switch_channel_test_flag(channel, CF_BRIDGE_ORIGINATOR)) {
		if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_BRIDGE) == SWITCH_STATUS_SUCCESS) {
			switch_core_session_t *other_session;

			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-A-Unique-ID", switch_core_session_get_uuid(session));
			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-B-Unique-ID", msg.string_arg);
			switch_channel_event_set_data(channel, event);
			if ((other_session = switch_core_session_locate(msg.string_arg))) {
				switch_channel_t *other_channel = switch_core_session_get_channel(other_session);

				switch_channel_set_bridge_time(other_channel);

				switch_event_add_presence_data_cols(other_channel, event, "Bridge-B-PD-");
				switch_core_session_rwunlock(other_session);
			}
			switch_event_fire(&event);
		}
	}

	if ((dmachine[0] = switch_core_session_get_dmachine(session, DIGIT_TARGET_SELF)) ||
		(dmachine[1] = switch_core_session_get_dmachine(session, DIGIT_TARGET_PEER))) {
		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
						  "%s not hibernating due to active digit parser, semi-hibernation engaged.\n", switch_channel_get_name(channel));

		while(switch_channel_ready(channel) && switch_channel_get_state(channel) == CS_HIBERNATE) {
			if (!switch_channel_test_flag(channel, CF_BROADCAST)) {
				if (dmachine[0]) {
					switch_ivr_dmachine_ping(dmachine[0], NULL);
				}
				if (dmachine[1]) {
					switch_ivr_dmachine_ping(dmachine[1], NULL);
				}
			}
			switch_yield(20000);
			switch_ivr_parse_all_messages(session);
		}
	}


	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t signal_bridge_on_hangup(switch_core_session_t *session)
{
	const char *uuid;
	switch_channel_t *channel = switch_core_session_get_channel(session);
	switch_core_session_t *other_session;
	switch_event_t *event;

	if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE))) {
		switch_channel_set_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE, NULL);
	}

	if (switch_channel_get_private(channel, "__bridge_term_key")) {
		switch_core_event_hook_remove_recv_dtmf(session, sb_on_dtmf);
		switch_channel_set_private(channel, "__bridge_term_key", NULL);
	}

	switch_channel_set_variable(channel, SWITCH_BRIDGE_VARIABLE, NULL);

	if (uuid && (other_session = switch_core_session_locate(uuid))) {
		switch_channel_t *other_channel = switch_core_session_get_channel(other_session);
		const char *sbv = switch_channel_get_variable(other_channel, SWITCH_SIGNAL_BRIDGE_VARIABLE);
		const char *var;

		if (!zstr(sbv) && !strcmp(sbv, switch_core_session_get_uuid(session))) {
			int hup = 1;

			switch_channel_set_variable(other_channel, SWITCH_SIGNAL_BRIDGE_VARIABLE, NULL);
			switch_channel_set_variable(other_channel, SWITCH_BRIDGE_VARIABLE, NULL);
			switch_channel_set_variable(other_channel, "call_uuid", switch_core_session_get_uuid(other_session));

			if (switch_channel_up_nosig(other_channel)) {
				if (switch_true(switch_channel_get_variable(other_channel, SWITCH_PARK_AFTER_BRIDGE_VARIABLE))) {
					switch_ivr_park_session(other_session);
					hup = 0;
				} else if ((var = switch_channel_get_variable(other_channel, SWITCH_TRANSFER_AFTER_BRIDGE_VARIABLE))) {
					transfer_after_bridge(other_session, var);
					hup = 0;
				}

				if (hup) {
					if (switch_channel_test_flag(other_channel, CF_BRIDGE_ORIGINATOR)) {
						if (switch_channel_test_flag(channel, CF_ANSWERED) &&
							switch_true(switch_channel_get_variable(other_channel, SWITCH_HANGUP_AFTER_BRIDGE_VARIABLE))) {

							if (switch_channel_test_flag(channel, CF_INTERCEPTED)) {
								switch_channel_set_flag(other_channel, CF_INTERCEPT);
							}
							switch_channel_hangup(other_channel, switch_channel_get_cause(channel));
						} else {
							if (!switch_channel_test_flag(channel, CF_ANSWERED)) {
								switch_channel_handle_cause(other_channel, switch_channel_get_cause(channel));
							}
							switch_channel_set_state(other_channel, CS_EXECUTE);
						}
					} else {
						switch_channel_hangup(other_channel, switch_channel_get_cause(channel));
					}
				}
			}
		}

		if (switch_channel_test_flag(channel, CF_BRIDGE_ORIGINATOR)) {
			switch_channel_clear_flag_recursive(channel, CF_BRIDGE_ORIGINATOR);
			if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_UNBRIDGE) == SWITCH_STATUS_SUCCESS) {
				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-A-Unique-ID", switch_core_session_get_uuid(session));
				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-B-Unique-ID", uuid);
				switch_event_add_presence_data_cols(other_channel, event, "Bridge-B-PD-");
				switch_channel_event_set_data(channel, event);
				switch_event_fire(&event);
			}
		}

		switch_core_session_rwunlock(other_session);
	} else {
		if (switch_channel_test_flag(channel, CF_BRIDGE_ORIGINATOR)) {
			switch_channel_clear_flag_recursive(channel, CF_BRIDGE_ORIGINATOR);
			if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_UNBRIDGE) == SWITCH_STATUS_SUCCESS) {
				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-A-Unique-ID", switch_core_session_get_uuid(session));
				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-B-Unique-ID", uuid);
				switch_channel_event_set_data(channel, event);
				switch_event_fire(&event);
			}
		}
	}

	return SWITCH_STATUS_SUCCESS;
}

static const switch_state_handler_table_t signal_bridge_state_handlers = {
	/*.on_init */ NULL,
	/*.on_routing */ NULL,
	/*.on_execute */ NULL,
	/*.on_hangup */ signal_bridge_on_hangup,
	/*.on_exchange_media */ NULL,
	/*.on_soft_execute */ NULL,
	/*.on_consume_media */ NULL,
	/*.on_hibernate */ signal_bridge_on_hibernate
};

static void check_bridge_export(switch_channel_t *channel, switch_channel_t *peer_channel)
{
	switch_caller_profile_t *originator_cp, *originatee_cp;

	originator_cp = switch_channel_get_caller_profile(channel);
	originatee_cp = switch_channel_get_caller_profile(peer_channel);

	originator_cp->callee_id_name = switch_core_strdup(originator_cp->pool, originatee_cp->callee_id_name);
	originator_cp->callee_id_number = switch_core_strdup(originator_cp->pool, originatee_cp->callee_id_number);


	switch_channel_process_export(peer_channel, channel, NULL, SWITCH_BRIDGE_EXPORT_VARS_VARIABLE);
	switch_channel_process_export(channel, peer_channel, NULL, SWITCH_BRIDGE_EXPORT_VARS_VARIABLE);
}

SWITCH_DECLARE(switch_status_t) switch_ivr_signal_bridge(switch_core_session_t *session, switch_core_session_t *peer_session)
{
	switch_channel_t *caller_channel = switch_core_session_get_channel(session);
	switch_channel_t *peer_channel = switch_core_session_get_channel(peer_session);
	switch_event_t *event;

	if (switch_channel_down_nosig(peer_channel)) {
		switch_channel_hangup(caller_channel, switch_channel_get_cause(peer_channel));
		return SWITCH_STATUS_FALSE;
	}

	if (!switch_channel_up_nosig(caller_channel)) {
		switch_channel_hangup(peer_channel, SWITCH_CAUSE_ORIGINATOR_CANCEL);
		return SWITCH_STATUS_FALSE;
	}

	check_bridge_export(caller_channel, peer_channel);

	switch_channel_set_flag_recursive(caller_channel, CF_SIGNAL_BRIDGE_TTL);
	switch_channel_set_flag_recursive(peer_channel, CF_SIGNAL_BRIDGE_TTL);

	switch_channel_set_variable(caller_channel, SWITCH_SIGNAL_BRIDGE_VARIABLE, switch_core_session_get_uuid(peer_session));
	switch_channel_set_variable(peer_channel, SWITCH_SIGNAL_BRIDGE_VARIABLE, switch_core_session_get_uuid(session));
	switch_channel_set_variable(peer_channel, "call_uuid", switch_core_session_get_uuid(session));

	switch_channel_set_flag_recursive(caller_channel, CF_BRIDGE_ORIGINATOR);
	switch_channel_clear_flag(peer_channel, CF_BRIDGE_ORIGINATOR);

	switch_channel_clear_state_handler(caller_channel, NULL);
	switch_channel_clear_state_handler(peer_channel, NULL);

	switch_channel_add_state_handler(caller_channel, &signal_bridge_state_handlers);
	switch_channel_add_state_handler(peer_channel, &signal_bridge_state_handlers);

	switch_channel_set_variable(caller_channel, "signal_bridge", "true");
	switch_channel_set_variable(peer_channel, "signal_bridge", "true");

	/* fire events that will change the data table from "show channels" */
	if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_EXECUTE) == SWITCH_STATUS_SUCCESS) {
		switch_channel_event_set_data(caller_channel, event);
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Application", "signal_bridge");
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Application-Data", switch_core_session_get_uuid(peer_session));
		switch_event_fire(&event);
	}

	if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_EXECUTE) == SWITCH_STATUS_SUCCESS) {
		switch_channel_event_set_data(peer_channel, event);
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Application", "signal_bridge");
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Application-Data", switch_core_session_get_uuid(session));
		switch_event_fire(&event);
	}

	switch_channel_set_state_flag(caller_channel, CF_RESET);
	switch_channel_set_state_flag(peer_channel, CF_RESET);

	switch_channel_set_state(caller_channel, CS_HIBERNATE);
	switch_channel_set_state(peer_channel, CS_HIBERNATE);

#if 0
	if (switch_channel_test_flag(caller_channel, CF_BRIDGED)) {
		switch_channel_set_flag(caller_channel, CF_TRANSFER);
		switch_channel_set_flag(peer_channel, CF_TRANSFER);
	}
#endif

	switch_ivr_bridge_display(session, peer_session);

	return SWITCH_STATUS_SUCCESS;
}

static void abort_call(switch_channel_t *caller_channel, switch_channel_t *peer_channel)
{
	switch_call_cause_t cause = switch_channel_get_cause(caller_channel);

	if (!cause) {
		cause = SWITCH_CAUSE_ORIGINATOR_CANCEL;
	}

	switch_channel_hangup(peer_channel, cause);
}

SWITCH_DECLARE(switch_status_t) switch_ivr_multi_threaded_bridge(switch_core_session_t *session,
																 switch_core_session_t *peer_session,
																 switch_input_callback_function_t input_callback, void *session_data,
																 void *peer_session_data)
{
	switch_ivr_bridge_data_t *a_leg = switch_core_session_alloc(session, sizeof(*a_leg));
	switch_ivr_bridge_data_t *b_leg = switch_core_session_alloc(peer_session, sizeof(*b_leg));
	switch_channel_t *caller_channel = switch_core_session_get_channel(session);
	switch_channel_t *peer_channel = switch_core_session_get_channel(peer_session);
	int stream_id = 0;
	switch_status_t status = SWITCH_STATUS_SUCCESS;
	switch_channel_state_t state;
	switch_event_t *event;
	int br = 0;
	int inner_bridge = switch_channel_test_flag(caller_channel, CF_INNER_BRIDGE);
	const char *var;
	switch_call_cause_t cause;
	switch_core_session_message_t msg = { 0 };

	if (switch_channel_test_flag(caller_channel, CF_PROXY_MODE)) {
		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Call has no media... Redirecting to signal bridge.\n");
		return switch_ivr_signal_bridge(session, peer_session);
	}

	check_bridge_export(caller_channel, peer_channel);

	switch_channel_set_flag_recursive(caller_channel, CF_MEDIA_BRIDGE_TTL);
	switch_channel_set_flag_recursive(peer_channel, CF_MEDIA_BRIDGE_TTL);

	switch_channel_set_flag_recursive(caller_channel, CF_BRIDGE_ORIGINATOR);
	switch_channel_clear_flag(peer_channel, CF_BRIDGE_ORIGINATOR);

	switch_channel_audio_sync(caller_channel);
	switch_channel_audio_sync(peer_channel);

	b_leg->session = peer_session;
	switch_copy_string(b_leg->b_uuid, switch_core_session_get_uuid(session), sizeof(b_leg->b_uuid));
	b_leg->stream_id = stream_id;
	b_leg->input_callback = input_callback;
	b_leg->session_data = peer_session_data;
	b_leg->clean_exit = 0;
	b_leg->other_leg_data = a_leg;

	a_leg->session = session;
	switch_copy_string(a_leg->b_uuid, switch_core_session_get_uuid(peer_session), sizeof(a_leg->b_uuid));
	a_leg->stream_id = stream_id;
	a_leg->input_callback = input_callback;
	a_leg->session_data = session_data;
	a_leg->clean_exit = 0;
	a_leg->other_leg_data = b_leg;

	switch_channel_add_state_handler(peer_channel, &audio_bridge_peer_state_handlers);

	if (switch_channel_test_flag(peer_channel, CF_ANSWERED) && !switch_channel_test_flag(caller_channel, CF_ANSWERED)) {
		switch_channel_pass_callee_id(peer_channel, caller_channel);
		switch_channel_answer(caller_channel);
	}

	if (switch_channel_test_flag(peer_channel, CF_ANSWERED) || switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA) ||
		switch_channel_test_flag(peer_channel, CF_RING_READY)) {
		const char *app, *data;

 		if (!switch_channel_ready(caller_channel)) {
			abort_call(caller_channel, peer_channel);
			goto done;
		}

		if (!switch_channel_test_flag(peer_channel, CF_ARRANGED_BRIDGE)) {
			switch_channel_set_state(peer_channel, CS_CONSUME_MEDIA);
		}

		switch_channel_set_variable(peer_channel, "call_uuid", switch_core_session_get_uuid(session));

		switch_channel_set_bridge_time(caller_channel);
		switch_channel_set_bridge_time(peer_channel);

		if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_BRIDGE) == SWITCH_STATUS_SUCCESS) {
			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-A-Unique-ID", switch_core_session_get_uuid(session));
			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-B-Unique-ID", switch_core_session_get_uuid(peer_session));
			switch_channel_event_set_data(caller_channel, event);
			switch_event_add_presence_data_cols(peer_channel, event, "Bridge-B-PD-");
			switch_event_fire(&event);
			br = 1;
		}

		if (switch_core_session_read_lock(peer_session) == SWITCH_STATUS_SUCCESS) {
			switch_channel_set_variable(caller_channel, SWITCH_BRIDGE_VARIABLE, switch_core_session_get_uuid(peer_session));
			switch_channel_set_variable(peer_channel, SWITCH_BRIDGE_VARIABLE, switch_core_session_get_uuid(session));
			switch_channel_set_variable(caller_channel, SWITCH_LAST_BRIDGE_VARIABLE, switch_core_session_get_uuid(peer_session));
			switch_channel_set_variable(peer_channel, SWITCH_LAST_BRIDGE_VARIABLE, switch_core_session_get_uuid(session));

			if (!switch_channel_ready(caller_channel)) {
				abort_call(caller_channel, peer_channel);
				switch_core_session_rwunlock(peer_session);
				goto done;
			}

			if (!switch_channel_media_ready(caller_channel) ||
				(!switch_channel_test_flag(peer_channel, CF_ANSWERED) && !switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA))) {
				if ((status = switch_ivr_wait_for_answer(session, peer_session)) != SWITCH_STATUS_SUCCESS || !switch_channel_ready(caller_channel)) {
					switch_channel_state_t w_state = switch_channel_get_state(caller_channel);
					switch_channel_hangup(peer_channel, SWITCH_CAUSE_ALLOTTED_TIMEOUT);
					if (w_state < CS_HANGUP && w_state != CS_ROUTING && w_state != CS_PARK &&
						!switch_channel_test_flag(caller_channel, CF_REDIRECT) && !switch_channel_test_flag(caller_channel, CF_TRANSFER) &&
						w_state != CS_EXECUTE) {
						const char *ext = switch_channel_get_variable(peer_channel, "original_destination_number");
						if (!ext) {
							ext = switch_channel_get_variable(peer_channel, "destination_number");
						}

						if (ext) {
							switch_ivr_session_transfer(session, ext, NULL, NULL);
						} else {
							switch_channel_hangup(caller_channel, SWITCH_CAUSE_ALLOTTED_TIMEOUT);
						}
					}
					abort_call(caller_channel, peer_channel);
					switch_core_session_rwunlock(peer_session);
					goto done;
				}
			}

			if (switch_channel_test_flag(peer_channel, CF_ANSWERED) && !switch_channel_test_flag(caller_channel, CF_ANSWERED)) {
				switch_channel_answer(caller_channel);
			}

			switch_channel_wait_for_flag(peer_channel, CF_BROADCAST, SWITCH_FALSE, 10000, caller_channel);
			switch_ivr_parse_all_events(peer_session);
			switch_ivr_parse_all_events(session);

			msg.message_id = SWITCH_MESSAGE_INDICATE_BRIDGE;
			msg.from = __FILE__;
			msg.string_arg = switch_core_session_strdup(peer_session, switch_core_session_get_uuid(session));

			if (switch_core_session_receive_message(peer_session, &msg) != SWITCH_STATUS_SUCCESS) {
				status = SWITCH_STATUS_FALSE;
				abort_call(caller_channel, peer_channel);
				switch_core_session_rwunlock(peer_session);
				goto done;
			}

			msg.string_arg = switch_core_session_strdup(session, switch_core_session_get_uuid(peer_session));
			if (switch_core_session_receive_message(session, &msg) != SWITCH_STATUS_SUCCESS) {
				status = SWITCH_STATUS_FALSE;
				abort_call(caller_channel, peer_channel);
				switch_core_session_rwunlock(peer_session);
				goto done;
			}

			switch_channel_set_variable(caller_channel, SWITCH_BRIDGE_CHANNEL_VARIABLE, switch_channel_get_name(peer_channel));
			switch_channel_set_variable(caller_channel, SWITCH_BRIDGE_UUID_VARIABLE, switch_core_session_get_uuid(peer_session));
			switch_channel_set_variable(caller_channel, SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(peer_session));
			switch_channel_set_variable(peer_channel, SWITCH_BRIDGE_CHANNEL_VARIABLE, switch_channel_get_name(caller_channel));
			switch_channel_set_variable(peer_channel, SWITCH_BRIDGE_UUID_VARIABLE, switch_core_session_get_uuid(session));
			switch_channel_set_variable(peer_channel, SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(session));

			if ((app = switch_channel_get_variable(caller_channel, "bridge_pre_execute_aleg_app"))) {
				switch_channel_set_variable(caller_channel, "bridge_pre_execute_app", app);

				if ((data = switch_channel_get_variable(caller_channel, "bridge_pre_execute_aleg_data"))) {
					switch_channel_set_variable(caller_channel, "bridge_pre_execute_data", data);
				}
			}

			if ((app = switch_channel_get_variable(caller_channel, "bridge_pre_execute_bleg_app"))) {
				switch_channel_set_variable(peer_channel, "bridge_pre_execute_app", app);

				if ((data = switch_channel_get_variable(caller_channel, "bridge_pre_execute_bleg_data"))) {
					switch_channel_set_variable(peer_channel, "bridge_pre_execute_data", data);
				}

			}

			switch_channel_set_private(peer_channel, "_bridge_", b_leg);

			if (switch_channel_test_flag(peer_channel, CF_ARRANGED_BRIDGE)) {
				switch_channel_clear_flag(peer_channel, CF_ARRANGED_BRIDGE);
			} else {
				switch_channel_set_state(peer_channel, CS_EXCHANGE_MEDIA);
			}
			
			audio_bridge_thread(NULL, (void *) a_leg);

			switch_channel_clear_flag_recursive(caller_channel, CF_BRIDGE_ORIGINATOR);

			switch_channel_stop_broadcast(peer_channel);


			while (switch_channel_get_state(peer_channel) == CS_EXCHANGE_MEDIA) {
				switch_ivr_parse_all_messages(session);
				switch_cond_next();
			}

			if (inner_bridge) {
				if (switch_channel_ready(caller_channel)) {
					switch_channel_set_flag(caller_channel, CF_BRIDGED);
				}

				if (switch_channel_ready(peer_channel)) {
					switch_channel_set_flag(peer_channel, CF_BRIDGED);
				}
			}

			if ((cause = switch_channel_get_cause(caller_channel))) {
				switch_channel_set_variable(peer_channel, SWITCH_BRIDGE_HANGUP_CAUSE_VARIABLE, switch_channel_cause2str(cause));
			}

			if ((cause = switch_channel_get_cause(peer_channel))) {
				switch_channel_set_variable(caller_channel, SWITCH_BRIDGE_HANGUP_CAUSE_VARIABLE, switch_channel_cause2str(cause));
			}

			if (switch_channel_down_nosig(peer_channel)) {
				switch_bool_t copy_xml_cdr = switch_true(switch_channel_get_variable(peer_channel, SWITCH_COPY_XML_CDR_VARIABLE));
				switch_bool_t copy_json_cdr = switch_true(switch_channel_get_variable(peer_channel, SWITCH_COPY_JSON_CDR_VARIABLE));

				if (copy_xml_cdr || copy_json_cdr) {
					char *cdr_text = NULL;

					switch_channel_wait_for_state(peer_channel, caller_channel, CS_DESTROY);

					if (copy_xml_cdr) {
						switch_xml_t cdr = NULL;

						if (switch_ivr_generate_xml_cdr(peer_session, &cdr) == SWITCH_STATUS_SUCCESS) {
							cdr_text = switch_xml_toxml(cdr, SWITCH_FALSE);
							switch_xml_free(cdr);
						}
					}
					if (copy_json_cdr) {
						cJSON *cdr = NULL;

						if (switch_ivr_generate_json_cdr(peer_session, &cdr, SWITCH_TRUE) == SWITCH_STATUS_SUCCESS) {
							cdr_text = cJSON_PrintUnformatted(cdr);
							cJSON_Delete(cdr);
						}
					}

					if (cdr_text) {
						switch_channel_set_variable(caller_channel, "b_leg_cdr", cdr_text);
						switch_channel_set_variable_name_printf(caller_channel, cdr_text, "b_leg_cdr_%s", switch_core_session_get_uuid(peer_session));
						switch_safe_free(cdr_text);
					}
				}

			}

			switch_core_session_rwunlock(peer_session);

		} else {
			status = SWITCH_STATUS_FALSE;
		}
	} else {
		status = SWITCH_STATUS_FALSE;
	}

	if (status != SWITCH_STATUS_SUCCESS) {
		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Bridge Failed %s->%s\n",
						  switch_channel_get_name(caller_channel), switch_channel_get_name(peer_channel)
			);
		switch_channel_hangup(peer_channel, SWITCH_CAUSE_NO_ANSWER);
	}

  done:

	switch_channel_set_variable(peer_channel, "call_uuid", switch_core_session_get_uuid(peer_session));

	if (br && switch_event_create(&event, SWITCH_EVENT_CHANNEL_UNBRIDGE) == SWITCH_STATUS_SUCCESS) {
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-A-Unique-ID", switch_core_session_get_uuid(session));
		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-B-Unique-ID", switch_core_session_get_uuid(peer_session));
		switch_channel_event_set_data(caller_channel, event);
		switch_event_add_presence_data_cols(peer_channel, event, "Bridge-B-PD-");
		switch_event_fire(&event);
	}

	msg.message_id = SWITCH_MESSAGE_INDICATE_UNBRIDGE;
	msg.from = __FILE__;
	msg.string_arg = switch_core_session_strdup(peer_session, switch_core_session_get_uuid(session));
	switch_core_session_receive_message(peer_session, &msg);

	msg.string_arg = switch_core_session_strdup(session, switch_core_session_get_uuid(peer_session));
	switch_core_session_receive_message(session, &msg);

	state = switch_channel_get_state(caller_channel);

	if (!switch_channel_test_flag(caller_channel, CF_TRANSFER) && !switch_channel_test_flag(caller_channel, CF_REDIRECT) &&
		!switch_channel_test_flag(caller_channel, CF_XFER_ZOMBIE) && !a_leg->clean_exit && !inner_bridge) {
		switch_call_cause_t cause = switch_channel_get_cause(peer_channel);
		const char *hup = switch_channel_get_variable(caller_channel, SWITCH_HANGUP_AFTER_BRIDGE_VARIABLE);
		int explicit = 0;
		int answered = 0;
		int early = 0;

		if (cause == SWITCH_CAUSE_NONE) {
			cause = SWITCH_CAUSE_NORMAL_CLEARING;
		}

		if (hup) {
			explicit = !strcasecmp(hup, "explicit");
		}

		if (!switch_channel_test_flag(peer_channel, CF_ANSWERED)) {
			switch_channel_handle_cause(caller_channel, cause);
		}

		if (explicit) {
			if (switch_channel_test_flag(peer_channel, CF_INTERCEPTED)) {
				switch_channel_set_flag(peer_channel, CF_INTERCEPT);
			}
			switch_channel_hangup(caller_channel, cause);
		}

		answered = switch_channel_test_flag(peer_channel, CF_ANSWERED);
		early = switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA);

		if ((state != CS_EXECUTE && state != CS_SOFT_EXECUTE && state != CS_PARK && state != CS_ROUTING) || ((answered || early) && state < CS_HANGUP)) {

			if (!switch_channel_test_flag(caller_channel, CF_TRANSFER)) {

				if ((answered && switch_true(switch_channel_get_variable(caller_channel, SWITCH_PARK_AFTER_BRIDGE_VARIABLE))) ||
					switch_true(switch_channel_get_variable(caller_channel, SWITCH_PARK_AFTER_EARLY_BRIDGE_VARIABLE))) {
					switch_ivr_park_session(session);
				} else if ((answered && (var = switch_channel_get_variable(caller_channel, SWITCH_TRANSFER_AFTER_BRIDGE_VARIABLE))) ||
						   (var = switch_channel_get_variable(caller_channel, SWITCH_TRANSFER_AFTER_EARLY_BRIDGE_VARIABLE))) {
					transfer_after_bridge(session, var);
				} else if (answered) {
					if (switch_true(hup)) {
						if (switch_channel_test_flag(peer_channel, CF_INTERCEPTED)) {
							switch_channel_set_flag(peer_channel, CF_INTERCEPT);
						}
						switch_channel_hangup(caller_channel, cause);
					}
				}
			}
		}
	}

	if (switch_channel_test_flag(caller_channel, CF_REDIRECT)) {
		if (switch_channel_test_flag(caller_channel, CF_RESET)) {
			switch_channel_clear_flag(caller_channel, CF_RESET);
		} else {
			state = switch_channel_get_state(caller_channel);
			if (!(state == CS_RESET || state == CS_PARK || state == CS_ROUTING || state == CS_EXECUTE)) {
				switch_channel_set_state(caller_channel, CS_RESET);
			}
		}
	}

	return status;
}

static void cleanup_proxy_mode_b(switch_core_session_t *session)
{
	switch_channel_t *channel = switch_core_session_get_channel(session);

	if (switch_channel_test_flag(channel, CF_PROXY_MODE) &&
		!switch_channel_test_flag(channel, CF_MEDIA_TRANS) && !switch_channel_test_flag(channel, CF_3P_MEDIA_REQUESTED)) {
		switch_ivr_media(switch_core_session_get_uuid(session), SMF_NONE);
	}
}


static void cleanup_proxy_mode_a(switch_core_session_t *session)
{
	switch_core_session_t *sbsession;
	switch_channel_t *channel = switch_core_session_get_channel(session);
	int done = 0;

	if (!switch_channel_test_flag(channel, CF_3P_MEDIA_REQUESTED) &&
		switch_channel_test_flag(channel, CF_PROXY_MODE) && !switch_channel_test_flag(channel, CF_3P_NOMEDIA_REQUESTED)) {
		if (switch_core_session_get_partner(session, &sbsession) == SWITCH_STATUS_SUCCESS) {
			switch_channel_t *sbchannel = switch_core_session_get_channel(sbsession);

			if (switch_channel_test_flag(sbchannel, CF_PROXY_MODE)) {
				/* Clear this now, otherwise will cause the one we're interested in to hang up too...*/
				switch_channel_set_variable(sbchannel, SWITCH_SIGNAL_BRIDGE_VARIABLE, NULL);
				switch_channel_hangup(sbchannel, SWITCH_CAUSE_ATTENDED_TRANSFER);
			} else {
				done = 1;
			}
			switch_core_session_rwunlock(sbsession);
		}
	}

	if (done) return;

	switch_channel_set_variable(channel, SWITCH_SIGNAL_BRIDGE_VARIABLE, NULL);
	switch_channel_set_variable(channel, SWITCH_BRIDGE_VARIABLE, NULL);
	switch_channel_set_variable(channel, SWITCH_BRIDGE_UUID_VARIABLE, NULL);

}


SWITCH_DECLARE(switch_status_t) switch_ivr_uuid_bridge(const char *originator_uuid, const char *originatee_uuid)
{
	switch_core_session_t *originator_session, *originatee_session, *swap_session;
	switch_channel_t *originator_channel, *originatee_channel, *swap_channel;
	switch_status_t status = SWITCH_STATUS_FALSE;
	switch_caller_profile_t *originator_cp, *originatee_cp;
	switch_channel_state_t state;

	if ((originator_session = switch_core_session_locate(originator_uuid))) {
		if ((originatee_session = switch_core_session_locate(originatee_uuid))) {
			originator_channel = switch_core_session_get_channel(originator_session);
			originatee_channel = switch_core_session_get_channel(originatee_session);

			switch_ivr_check_hold(originator_session);
			switch_ivr_check_hold(originatee_session);
	

			if (switch_channel_test_flag(originator_channel, CF_LEG_HOLDING)) {
				switch_channel_set_flag(originator_channel, CF_HOLD_ON_BRIDGE);
			}

			if (switch_channel_test_flag(originatee_channel, CF_LEG_HOLDING)) {
				switch_channel_set_flag(originatee_channel, CF_HOLD_ON_BRIDGE);
			}


			if (switch_channel_direction(originator_channel) == SWITCH_CALL_DIRECTION_OUTBOUND && !switch_channel_test_flag(originator_channel, CF_DIALPLAN)) {
				if (!switch_channel_test_flag(originator_channel, CF_RECOVERING_BRIDGE)) {
					switch_channel_flip_cid(originator_channel);
				}
				switch_channel_set_flag(originator_channel, CF_DIALPLAN);
			}

			if (switch_channel_down_nosig(originator_channel)) {
				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(originator_session), SWITCH_LOG_DEBUG, "%s is hungup refusing to bridge.\n", switch_channel_get_name(originatee_channel));
				switch_core_session_rwunlock(originator_session);
				switch_core_session_rwunlock(originatee_session);
				return SWITCH_STATUS_FALSE;
			}

			if (!switch_channel_media_up(originator_channel)) {
				if (switch_channel_media_up(originatee_channel)) {
					swap_session = originator_session;
					originator_session = originatee_session;
					originatee_session = swap_session;

					swap_channel = originator_channel;
					originator_channel = originatee_channel;
					originatee_channel = swap_channel;
					switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(originatee_session), SWITCH_LOG_WARNING, "reversing order of channels so this will work!\n");
				} else {
					switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(originator_session), SWITCH_LOG_CRIT, "Neither channel is answered, cannot bridge them.\n");
					switch_core_session_rwunlock(originator_session);
					switch_core_session_rwunlock(originatee_session);
					return SWITCH_STATUS_FALSE;
				}
			}

			if (switch_channel_direction(originatee_channel) == SWITCH_CALL_DIRECTION_OUTBOUND && switch_channel_test_flag(originatee_channel, CF_DIALPLAN)) {
				switch_channel_clear_flag(originatee_channel, CF_DIALPLAN);
			}

			cleanup_proxy_mode_a(originator_session);
			cleanup_proxy_mode_a(originatee_session);

			/* override transmit state for originator_channel to bridge to originatee_channel
			 * install pointer to originatee_session into originator_channel
			 * set CF_TRANSFER on both channels and change state to CS_SOFT_EXECUTE to
			 * interrupt anything they are already doing.
			 * originatee_session will fall asleep and originator_session will bridge to it
			 */

			switch_channel_set_flag(originator_channel, CF_REDIRECT);
			switch_channel_set_flag(originatee_channel, CF_REDIRECT);


			switch_channel_set_variable(originator_channel, SWITCH_UUID_BRIDGE, switch_core_session_get_uuid(originatee_session));
			switch_channel_set_variable(originator_channel, SWITCH_BRIDGE_CHANNEL_VARIABLE, switch_channel_get_name(originatee_channel));
			switch_channel_set_variable(originator_channel, SWITCH_BRIDGE_UUID_VARIABLE, switch_core_session_get_uuid(originatee_session));
			switch_channel_set_variable(originator_channel, SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(originatee_session));
			switch_channel_set_variable(originatee_channel, SWITCH_BRIDGE_CHANNEL_VARIABLE, switch_channel_get_name(originator_channel));
			switch_channel_set_variable(originatee_channel, SWITCH_BRIDGE_UUID_VARIABLE, switch_core_session_get_uuid(originator_session));
			switch_channel_set_variable(originatee_channel, SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(originator_session));


			originator_cp = switch_channel_get_caller_profile(originator_channel);
			originatee_cp = switch_channel_get_caller_profile(originatee_channel);



			if (switch_channel_outbound_display(originator_channel)) {
				switch_channel_invert_cid(originator_channel);

				if (switch_channel_direction(originator_channel) == SWITCH_CALL_DIRECTION_INBOUND) {
					switch_channel_clear_flag(originatee_channel, CF_BLEG);
				}
			}

			if (switch_channel_inbound_display(originatee_channel)) {
				switch_channel_invert_cid(originatee_channel);

				if (switch_channel_direction(originatee_channel) == SWITCH_CALL_DIRECTION_INBOUND) {
					switch_channel_set_flag(originatee_channel, CF_BLEG);
				}

			}


			switch_channel_set_variable(originatee_channel, "original_destination_number", originatee_cp->destination_number);
			switch_channel_set_variable(originatee_channel, "original_caller_id_name", originatee_cp->caller_id_name);
			switch_channel_set_variable(originatee_channel, "original_caller_id_number", originatee_cp->caller_id_number);

			switch_channel_set_variable(originator_channel, "original_destination_number", originator_cp->destination_number);
			switch_channel_set_variable(originator_channel, "original_caller_id_name", originator_cp->caller_id_name);
			switch_channel_set_variable(originator_channel, "original_caller_id_number", originator_cp->caller_id_number);

			switch_channel_step_caller_profile(originatee_channel);
			switch_channel_step_caller_profile(originator_channel);

			originator_cp = switch_channel_get_caller_profile(originator_channel);
			originatee_cp = switch_channel_get_caller_profile(originatee_channel);


#ifdef DEEP_DEBUG_CID
			{
				switch_event_t *event;

				if (switch_event_create_plain(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
					//switch_channel_event_set_basic_data(originator_channel, event);
					switch_caller_profile_event_set_data(originator_cp, "ORIGINATOR", event);
					switch_caller_profile_event_set_data(originatee_cp, "ORIGINATEE", event);
					DUMP_EVENT(event);
					switch_event_destroy(&event);
				}
			}
#endif

			switch_channel_set_originator_caller_profile(originatee_channel, switch_caller_profile_clone(originatee_session, originator_cp));
			switch_channel_set_originatee_caller_profile(originator_channel, switch_caller_profile_clone(originator_session, originatee_cp));

			originator_cp->callee_id_name = switch_core_strdup(originator_cp->pool, originatee_cp->callee_id_name);
			originator_cp->callee_id_number = switch_core_strdup(originator_cp->pool, originatee_cp->callee_id_number);

			originatee_cp->caller_id_name = switch_core_strdup(originatee_cp->pool, originator_cp->caller_id_name);
			originatee_cp->caller_id_number = switch_core_strdup(originatee_cp->pool, originator_cp->caller_id_number);

#ifdef DEEP_DEBUG_CID
			{
				switch_event_t *event;

				if (switch_event_create_plain(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
					//switch_channel_event_set_basic_data(originator_channel, event);
					switch_caller_profile_event_set_data(originator_cp, "POST-ORIGINATOR", event);
					switch_caller_profile_event_set_data(originatee_cp, "POST-ORIGINATEE", event);
					DUMP_EVENT(event);
					switch_event_destroy(&event);
				}
			}
#endif

			switch_channel_stop_broadcast(originator_channel);
			switch_channel_stop_broadcast(originatee_channel);

			switch_channel_set_flag(originator_channel, CF_TRANSFER);
			switch_channel_set_flag(originatee_channel, CF_TRANSFER);


			switch_channel_clear_flag(originator_channel, CF_ORIGINATING);
			switch_channel_clear_flag(originatee_channel, CF_ORIGINATING);


			originator_cp->transfer_source = switch_core_sprintf(originator_cp->pool,
																 "%ld:%s:uuid_br:%s", (long)switch_epoch_time_now(NULL), originator_cp->uuid_str,
																 switch_core_session_get_uuid(originatee_session));
			switch_channel_add_variable_var_check(originator_channel, SWITCH_TRANSFER_HISTORY_VARIABLE,
												  originator_cp->transfer_source, SWITCH_FALSE, SWITCH_STACK_PUSH);
			switch_channel_set_variable(originator_channel, SWITCH_TRANSFER_SOURCE_VARIABLE, originator_cp->transfer_source);


			originatee_cp->transfer_source = switch_core_sprintf(originatee_cp->pool,
																 "%ld:%s:uuid_br:%s", (long)switch_epoch_time_now(NULL), originatee_cp->uuid_str,
																 switch_core_session_get_uuid(originator_session));
			switch_channel_add_variable_var_check(originatee_channel, SWITCH_TRANSFER_HISTORY_VARIABLE,
												  originatee_cp->transfer_source, SWITCH_FALSE, SWITCH_STACK_PUSH);
			switch_channel_set_variable(originatee_channel, SWITCH_TRANSFER_SOURCE_VARIABLE, originatee_cp->transfer_source);

			/* change the states and let the chips fall where they may */

			//switch_channel_set_variable(originator_channel, SWITCH_PARK_AFTER_BRIDGE_VARIABLE, NULL);
			//switch_channel_set_variable(originatee_channel, SWITCH_PARK_AFTER_BRIDGE_VARIABLE, NULL);
			switch_channel_clear_state_handler(originator_channel, NULL);
			switch_channel_clear_state_handler(originatee_channel, NULL);



			switch_channel_clear_state_flag(originator_channel, CF_BRIDGE_ORIGINATOR);
			switch_channel_clear_state_flag(originatee_channel, CF_BRIDGE_ORIGINATOR);

			switch_channel_clear_flag(originator_channel, CF_UUID_BRIDGE_ORIGINATOR);
			switch_channel_clear_flag(originatee_channel, CF_UUID_BRIDGE_ORIGINATOR);
			switch_channel_set_state_flag(originator_channel, CF_UUID_BRIDGE_ORIGINATOR);

			switch_channel_add_state_handler(originator_channel, &uuid_bridge_state_handlers);
			switch_channel_add_state_handler(originatee_channel, &uuid_bridge_state_handlers);

			state = switch_channel_get_state(originator_channel);
			switch_channel_set_state(originator_channel, state == CS_HIBERNATE ? CS_CONSUME_MEDIA : CS_HIBERNATE);
			state = switch_channel_get_state(originatee_channel);
			switch_channel_set_state(originatee_channel, state == CS_HIBERNATE ? CS_CONSUME_MEDIA : CS_HIBERNATE);

			status = SWITCH_STATUS_SUCCESS;

			//switch_ivr_bridge_display(originator_session, originatee_session);

			/* release the read locks we have on the channels */
			switch_core_session_rwunlock(originator_session);
			switch_core_session_rwunlock(originatee_session);

		} else {
			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(originator_session), SWITCH_LOG_DEBUG, "originatee uuid %s is not present\n", originatee_uuid);
			switch_core_session_rwunlock(originator_session);
		}
	} else {
		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(originator_session), SWITCH_LOG_DEBUG, "originator uuid %s is not present\n", originator_uuid);
	}

	return status;
}

SWITCH_DECLARE(switch_status_t) switch_ivr_find_bridged_uuid(const char *uuid, char *b_uuid, switch_size_t blen)
{
	switch_core_session_t *rsession;
	switch_status_t status = SWITCH_STATUS_FALSE;

	switch_assert(uuid);

	if ((rsession = switch_core_session_locate(uuid))) {
		switch_channel_t *rchannel = switch_core_session_get_channel(rsession);
		const char *brto;

		if ((brto = switch_channel_get_variable(rchannel, SWITCH_ORIGINATE_SIGNAL_BOND_VARIABLE)) ||
			(brto = switch_channel_get_partner_uuid(rchannel))) {
			switch_copy_string(b_uuid, brto, blen);
			status = SWITCH_STATUS_SUCCESS;
		}
		switch_core_session_rwunlock(rsession);
	}

	return status;

}

SWITCH_DECLARE(switch_status_t) switch_ivr_intercept_session(switch_core_session_t *session, const char *uuid, switch_bool_t bleg)
{
	switch_core_session_t *rsession, *bsession = NULL;
	switch_channel_t *channel, *rchannel, *bchannel = NULL;
	const char *buuid, *var;
	char brto[SWITCH_UUID_FORMATTED_LENGTH + 1] = "";
	switch_status_t status = SWITCH_STATUS_FALSE;

	if (bleg) {
		if (switch_ivr_find_bridged_uuid(uuid, brto, sizeof(brto)) == SWITCH_STATUS_SUCCESS) {
			uuid = switch_core_session_strdup(session, brto);
		} else {
			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "no uuid bridged to %s\n", uuid);
			return status;
		}
	}

	if (zstr(uuid) || !(rsession = switch_core_session_locate(uuid))) {
		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "no uuid %s\n", uuid);
		return status;
	}

	channel = switch_core_session_get_channel(session);
	rchannel = switch_core_session_get_channel(rsession);
	buuid = switch_channel_get_partner_uuid(rchannel);

	if (zstr(buuid) || !strcasecmp(buuid, switch_core_session_get_uuid(session))) {
		buuid = NULL;
	}

	if ((var = switch_channel_get_variable(channel, "intercept_unbridged_only")) && switch_true(var)) {
		if ((switch_channel_test_flag(rchannel, CF_BRIDGED))) {
			switch_core_session_rwunlock(rsession);
			return status;
		}
	}

	if ((var = switch_channel_get_variable(channel, "intercept_unanswered_only")) && switch_true(var)) {
		if ((switch_channel_test_flag(rchannel, CF_ANSWERED))) {
			switch_core_session_rwunlock(rsession);
			return status;
		}
	}

	switch_channel_answer(channel);

	if (!zstr(buuid)) {
		if ((bsession = switch_core_session_locate(buuid))) {
			bchannel = switch_core_session_get_channel(bsession);
			switch_channel_set_flag(bchannel, CF_INTERCEPT);
		}
	}

	if (!switch_channel_test_flag(rchannel, CF_ANSWERED)) {
		switch_channel_answer(rchannel);
	}

	switch_channel_mark_hold(rchannel, SWITCH_FALSE);

	switch_channel_set_state_flag(rchannel, CF_TRANSFER);
	switch_channel_set_state(rchannel, CS_PARK);

	if (bchannel) {
		switch_channel_set_variable(bchannel, "park_after_bridge", "true");
	}

	if ((var = switch_channel_get_variable(channel, "intercept_pre_bond")) && switch_true(var)) {
		switch_channel_set_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE, uuid);
		switch_channel_set_variable_partner(channel, SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(session));
	}

	switch_channel_set_flag(rchannel, CF_INTERCEPTED);
	status = switch_ivr_uuid_bridge(switch_core_session_get_uuid(session), uuid);
	switch_core_session_rwunlock(rsession);

	if (bsession) {
		switch_channel_hangup(bchannel, SWITCH_CAUSE_PICKED_OFF);
		switch_core_session_rwunlock(bsession);
	}

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