diff --git a/src/include/switch_apr.h b/src/include/switch_apr.h index cb4c2a2a49..ffdf3a0bd5 100644 --- a/src/include/switch_apr.h +++ b/src/include/switch_apr.h @@ -1352,6 +1352,18 @@ DoxyDefine(apr_status_t switch_queue_trypop(switch_queue_t *queue, void **data); DoxyDefine(apr_status_t switch_queue_trypush(switch_queue_t *queue, void *data);) #define switch_queue_trypush apr_queue_trypush + +typedef apr_thread_rwlock_t switch_thread_rwlock_t; + +#define switch_thread_rwlock_create apr_thread_rwlock_create +#define switch_thread_rwlock_destroy apr_thread_rwlock_destroy +#define switch_thread_rwlock_pool_get apr_thread_rwlock_pool_get +#define switch_thread_rwlock_rdlock apr_thread_rwlock_rdlock +#define switch_thread_rwlock_tryrdlock apr_thread_rwlock_tryrdlock +#define switch_thread_rwlock_trywrlock apr_thread_rwlock_trywrlock +#define switch_thread_rwlock_unlock apr_thread_rwlock_unlock +#define switch_thread_rwlock_wrlock apr_thread_rwlock_wrlock + /** * @defgroup switch_thread_mutex Thread Mutex Routines * @ingroup switch_apr diff --git a/src/include/switch_caller.h b/src/include/switch_caller.h index 5d2809df93..7b09ec6462 100644 --- a/src/include/switch_caller.h +++ b/src/include/switch_caller.h @@ -87,6 +87,9 @@ struct switch_caller_profile { char *chan_name; /*! unique id */ char *uuid; + /*! context */ + char *context; + struct switch_caller_profile *next; }; /*! \brief An Abstract Representation of a dialplan Application */ @@ -162,6 +165,7 @@ SWITCH_DECLARE(switch_caller_profile *) switch_caller_profile_new(switch_memory_ char *ani2, char *rdnis, char *source, + char *context, char *destination_number); /*! diff --git a/src/include/switch_channel.h b/src/include/switch_channel.h index 9cba98e080..34c805c27c 100644 --- a/src/include/switch_channel.h +++ b/src/include/switch_channel.h @@ -41,14 +41,20 @@ #ifdef __cplusplus extern "C" { #endif +#ifdef __FORMATBUG +} +#endif #include -typedef struct { +struct switch_channel_timetable { switch_time_t created; switch_time_t answered; switch_time_t hungup; -} switch_channel_timetable_t; + struct switch_channel_timetable *next; +}; + +typedef struct switch_channel_timetable switch_channel_timetable_t; /** * @defgroup switch_channel Channel Functions diff --git a/src/include/switch_core.h b/src/include/switch_core.h index acd650a792..9366e54d84 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -118,6 +118,28 @@ SWITCH_DECLARE(switch_status) switch_core_destroy(void); ///\} +///\defgroup sh Read/Write Locking +///\ingroup core1 +///\{ +/*! + \brief Acquire a read lock on the session + \param session the session to acquire from + \return success if it is safe to read from the session +*/ +SWITCH_DECLARE(switch_status) switch_core_session_read_lock(switch_core_session *session); + +/*! + \brief Acquire a write lock on the session + \param session the session to acquire from +*/ +SWITCH_DECLARE(void) switch_core_session_write_lock(switch_core_session *session); + +/*! + \brief Unlock a read or write lock on as given session + \param session the session +*/ +SWITCH_DECLARE(void) switch_core_session_rwunlock(switch_core_session *session); +///\} ///\defgroup sh State Handlers ///\ingroup core1 @@ -277,6 +299,7 @@ SWITCH_DECLARE(char *) switch_core_session_get_uuid(switch_core_session *session \brief Locate a session based on it's uuiid \param uuid_str the unique id of the session you want to find \return the session or NULL + \note if the session was located it will have a read lock obtained which will need to be released with switch_core_session_rwunlock() */ SWITCH_DECLARE(switch_core_session *) switch_core_session_locate(char *uuid_str); diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index ced4ad0c26..a3e474b993 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -167,6 +167,16 @@ SWITCH_DECLARE(switch_status) switch_ivr_multi_threaded_bridge(switch_core_sessi void *session_data, void *peer_session_data); + +/*! + \brief Transfer an existing session to another location + \param session the session to transfer + \param extension the new extension + \param dialplan the new dialplan (OPTIONAL, may be NULL) + \param context the new context (OPTIONAL, may be NULL) +*/ +SWITCH_DECLARE(switch_status) switch_ivr_session_transfer(switch_core_session *session, char *extension, char *dialplan, char *context); + /** @} */ #ifdef __cplusplus diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h index f9b8e34a3a..74a766d635 100644 --- a/src/include/switch_module_interfaces.h +++ b/src/include/switch_module_interfaces.h @@ -60,6 +60,8 @@ struct switch_state_handler_table { switch_state_handler on_loopback; /*! executed when the state changes to transmit*/ switch_state_handler on_transmit; + /*! executed when the state changes to hold*/ + switch_state_handler on_hold; }; /*! \brief Node in which to store custom outgoing channel callback hooks */ diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 0dab55dc16..b6c36cd74c 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -297,6 +297,7 @@ CS_RING - Channel is looking for a dialplan CS_TRANSMIT - Channel is in a passive transmit state CS_EXECUTE - Channel is executing it's dialplan CS_LOOPBACK - Channel is in loopback +CS_HOLD - Channel is on hold CS_HANGUP - Channel is flagged for hangup and ready to end CS_DONE - Channel is ready to be destroyed and out of the state machine @@ -308,6 +309,7 @@ typedef enum { CS_TRANSMIT, CS_EXECUTE, CS_LOOPBACK, + CS_HOLD, CS_HANGUP, CS_DONE } switch_channel_state; @@ -325,6 +327,8 @@ CF_ORIGINATOR = (1 << 3) - Channel is an originator CF_TRANSFER = (1 << 4) - Channel is being transfered CF_ACCEPT_CNG = (1 << 5) - Channel will accept CNG frames CF_LOCK_THREAD = (1 << 6) - Prevent the channel thread from exiting while this flag is set +CF_BRIDGED = (1 << 7) - Channel in a bridge +CF_HOLD = (1 << 8) - Channel is on hold */ @@ -335,7 +339,9 @@ typedef enum { CF_ORIGINATOR = (1 << 3), CF_TRANSFER = (1 << 4), CF_ACCEPT_CNG = (1 << 5), - CF_LOCK_THREAD = (1 << 6) + CF_LOCK_THREAD = (1 << 6), + CF_BRIDGED = (1 << 7), + CF_HOLD = (1 << 8) } switch_channel_flag; diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h index bbcb85486b..26629d93ac 100644 --- a/src/include/switch_utils.h +++ b/src/include/switch_utils.h @@ -180,6 +180,8 @@ SWITCH_DECLARE(char *) switch_cut_path(char *in); SWITCH_DECLARE(char *) switch_string_replace(const char *string, const char *search, const char *replace); SWITCH_DECLARE(switch_status) switch_string_match(const char *string, size_t string_len, const char *search, size_t search_len); +#define SWITCH_READ_ACCEPTABLE(status) status == SWITCH_STATUS_SUCCESS || status == SWITCH_STATUS_BREAK + #ifdef __cplusplus } #endif diff --git a/src/mod/applications/mod_bridgecall/mod_bridgecall.c b/src/mod/applications/mod_bridgecall/mod_bridgecall.c index 0bddb79ea9..0e1fcbcc90 100644 --- a/src/mod/applications/mod_bridgecall/mod_bridgecall.c +++ b/src/mod/applications/mod_bridgecall/mod_bridgecall.c @@ -64,6 +64,7 @@ static void audio_bridge_function(switch_core_session *session, char *data) NULL, caller_caller_profile->rdnis, caller_caller_profile->source, + caller_caller_profile->context, chan_data); diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index 3c32bc9e16..a5c53fd7d9 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -49,6 +49,7 @@ static switch_status kill_function(char *dest, char *out, size_t outlen) switch_channel *channel = switch_core_session_get_channel(session); switch_core_session_kill_channel(session, SWITCH_SIG_KILL); switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + switch_core_session_rwunlock(session); snprintf(out, outlen, "OK\n"); } else { snprintf(out, outlen, "No Such Channel!\n"); @@ -58,11 +59,96 @@ static switch_status kill_function(char *dest, char *out, size_t outlen) } +static switch_status transfer_function(char *cmd, char *out, size_t outlen) +{ + switch_core_session *session = NULL; + char *argv[4] = {0}; + int argc = 0; + + argc = switch_separate_string(cmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + if (argc < 2 || argc > 4) { + snprintf(out, outlen, "Invalid Parameters\n"); + } else { + char *uuid = argv[0]; + char *dest = argv[1]; + char *dp = argv[2]; + char *context = argv[3]; + + if ((session = switch_core_session_locate(uuid))) { + + if (switch_ivr_session_transfer(session, dest, dp, context) == SWITCH_STATUS_SUCCESS) { + snprintf(out, outlen, "OK\n"); + } else { + snprintf(out, outlen, "ERROR\n"); + } + + switch_core_session_rwunlock(session); + + } else { + snprintf(out, outlen, "No Such Channel!\n"); + } + } + + return SWITCH_STATUS_SUCCESS; +} + + + + +static switch_status pause_function(char *cmd, char *out, size_t outlen) +{ + switch_core_session *session = NULL; + char *argv[4] = {0}; + int argc = 0; + + argc = switch_separate_string(cmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + if (argc < 2) { + snprintf(out, outlen, "Invalid Parameters\n"); + } else { + char *uuid = argv[0]; + char *dest = argv[1]; + + if ((session = switch_core_session_locate(uuid))) { + switch_channel *channel = switch_core_session_get_channel(session); + + if (!strcasecmp(dest, "on")) { + switch_channel_set_flag(channel, CF_HOLD); + } else { + switch_channel_clear_flag(channel, CF_HOLD); + } + + switch_core_session_rwunlock(session); + + } else { + snprintf(out, outlen, "No Such Channel!\n"); + } + } + + return SWITCH_STATUS_SUCCESS; +} + + +static struct switch_api_interface pause_api_interface = { + /*.interface_name */ "pause", + /*.desc */ "Pause", + /*.function */ pause_function, + /*.next */ NULL +}; + +static struct switch_api_interface transfer_api_interface = { + /*.interface_name */ "transfer", + /*.desc */ "Transfer", + /*.function */ transfer_function, + /*.next */ &pause_api_interface +}; + static struct switch_api_interface load_api_interface = { /*.interface_name */ "load", /*.desc */ "Load Modile", /*.function */ load_function, - /*.next */ NULL + /*.next */ &transfer_api_interface }; diff --git a/src/mod/dialplans/mod_pcre/mod_pcre.c b/src/mod/dialplans/mod_pcre/mod_pcre.c index c760c51edc..a584ed41ed 100644 --- a/src/mod/dialplans/mod_pcre/mod_pcre.c +++ b/src/mod/dialplans/mod_pcre/mod_pcre.c @@ -87,6 +87,12 @@ static switch_caller_extension *dialplan_hunt(switch_core_session *session) if (strcasecmp(exten_name, caller_profile->source)) { skip = 1; } + } else if (*exten_name == 'c' && *(exten_name+1) == ':') { + exten_name += 2; + if (strcasecmp(exten_name, caller_profile->context)) { + skip = 1; + } + } } diff --git a/src/mod/endpoints/mod_dingaling/mod_dingaling.c b/src/mod/endpoints/mod_dingaling/mod_dingaling.c index 346dcd6cdd..edfc951607 100644 --- a/src/mod/endpoints/mod_dingaling/mod_dingaling.c +++ b/src/mod/endpoints/mod_dingaling/mod_dingaling.c @@ -89,6 +89,7 @@ struct mdl_profile { char *extip; char *lanaddr; char *exten; + char *context; ldl_handle_t *handle; unsigned int flags; }; @@ -1246,6 +1247,8 @@ static switch_status load_config(void) profile->lanaddr = switch_core_strdup(module_pool, val); } else if (!strcmp(var, "exten")) { profile->exten = switch_core_strdup(module_pool, val); + } else if (!strcmp(var, "context")) { + profile->context = switch_core_strdup(module_pool, val); } else if (!strcmp(var, "vad")) { if (!strcasecmp(val, "in")) { switch_set_flag(profile, TFLAG_VAD_IN); @@ -1341,6 +1344,7 @@ static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsessi NULL, NULL, (char *)modname, + profile->context, profile->exten)) != 0) { char name[128]; snprintf(name, sizeof(name), "DingaLing/%s-%04x", tech_pvt->caller_profile->destination_number, diff --git a/src/mod/endpoints/mod_exosip/mod_exosip.c b/src/mod/endpoints/mod_exosip/mod_exosip.c index 07d77f2a38..9394849247 100644 --- a/src/mod/endpoints/mod_exosip/mod_exosip.c +++ b/src/mod/endpoints/mod_exosip/mod_exosip.c @@ -1193,6 +1193,7 @@ static switch_status exosip_create_call(eXosip_event_t * event) NULL, NULL, (char *)modname, + NULL, event->request->req_uri->username)) != 0) { switch_channel_set_caller_profile(channel, tech_pvt->caller_profile); } diff --git a/src/mod/endpoints/mod_iax/mod_iax.c b/src/mod/endpoints/mod_iax/mod_iax.c index 828caa4011..a34e7a1100 100644 --- a/src/mod/endpoints/mod_iax/mod_iax.c +++ b/src/mod/endpoints/mod_iax/mod_iax.c @@ -1004,6 +1004,7 @@ SWITCH_MOD_DECLARE(switch_status) switch_module_runtime(void) NULL, NULL, (char *)modname, + NULL, iaxevent->ies.called_number)) != 0) { char name[128]; snprintf(name, sizeof(name), "IAX/%s-%04x", tech_pvt->caller_profile->destination_number, diff --git a/src/mod/endpoints/mod_portaudio/mod_portaudio.c b/src/mod/endpoints/mod_portaudio/mod_portaudio.c index c503122d43..da348c1d6b 100644 --- a/src/mod/endpoints/mod_portaudio/mod_portaudio.c +++ b/src/mod/endpoints/mod_portaudio/mod_portaudio.c @@ -811,7 +811,7 @@ static switch_status place_call(char *dest, char *out, size_t outlen) if ((tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session), globals.dialplan, globals.cid_name, - globals.cid_num, NULL, NULL, NULL, NULL, (char *)modname, dest)) != 0) { + globals.cid_num, NULL, NULL, NULL, NULL, (char *)modname, NULL, dest)) != 0) { char name[128]; snprintf(name, sizeof(name), "PortAudio/%s-%04x", tech_pvt->caller_profile->destination_number ? tech_pvt->caller_profile-> diff --git a/src/mod/endpoints/mod_wanpipe/mod_wanpipe.c b/src/mod/endpoints/mod_wanpipe/mod_wanpipe.c index 53aa5b7f1e..2484198483 100644 --- a/src/mod/endpoints/mod_wanpipe/mod_wanpipe.c +++ b/src/mod/endpoints/mod_wanpipe/mod_wanpipe.c @@ -1135,6 +1135,7 @@ static int on_ring(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri NULL, NULL, (char *)modname, + NULL, event->ring.callednum))) { switch_channel_set_caller_profile(channel, tech_pvt->caller_profile); } diff --git a/src/mod/endpoints/mod_woomera/mod_woomera.c b/src/mod/endpoints/mod_woomera/mod_woomera.c index 758e5f7ca1..c95731a5a2 100644 --- a/src/mod/endpoints/mod_woomera/mod_woomera.c +++ b/src/mod/endpoints/mod_woomera/mod_woomera.c @@ -1068,7 +1068,7 @@ static void *woomera_channel_thread_run(switch_thread *thread, void *obj) if ((tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session), tech_pvt->profile->dialplan, - cid_name, cid_num, ip, NULL, NULL, NULL, (char *)modname, exten)) != 0) { + cid_name, cid_num, ip, NULL, NULL, NULL, (char *)modname, NULL, exten)) != 0) { char name[128]; snprintf(name, sizeof(name), "Woomera/%s-%04x", tech_pvt->caller_profile->destination_number, rand() & 0xffff); diff --git a/src/mod/event_handlers/mod_event_test/mod_event_test.c b/src/mod/event_handlers/mod_event_test/mod_event_test.c index afc7bcf4c7..62646ed425 100644 --- a/src/mod/event_handlers/mod_event_test/mod_event_test.c +++ b/src/mod/event_handlers/mod_event_test/mod_event_test.c @@ -45,7 +45,7 @@ static void event_handler(switch_event *event) return; default: switch_event_serialize(event, buf, sizeof(buf), NULL); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "\nEVENT\n--------------------------------\n%s\n", buf); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "\nEVENT\n--------------------------------\n%s\n", buf); break; } } diff --git a/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c b/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c index 9cda046007..4cc582cf18 100644 --- a/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c +++ b/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c @@ -980,6 +980,7 @@ static JSBool session_construct(JSContext *cx, JSObject *obj, uintN argc, jsval char *ani = ""; char *ani2 = ""; char *rdnis = ""; + char *context = ""; *rval = BOOLEAN_TO_JSVAL( JS_FALSE ); @@ -997,30 +998,34 @@ static JSBool session_construct(JSContext *cx, JSObject *obj, uintN argc, jsval dialplan = JS_GetStringBytes(JS_ValueToString(cx, argv[3])); } if (argc > 4) { - cid_name = JS_GetStringBytes(JS_ValueToString(cx, argv[4])); + context = JS_GetStringBytes(JS_ValueToString(cx, argv[4])); } if (argc > 5) { - cid_num = JS_GetStringBytes(JS_ValueToString(cx, argv[5])); + cid_name = JS_GetStringBytes(JS_ValueToString(cx, argv[5])); } if (argc > 6) { - network_addr = JS_GetStringBytes(JS_ValueToString(cx, argv[6])); + cid_num = JS_GetStringBytes(JS_ValueToString(cx, argv[6])); } if (argc > 7) { - ani = JS_GetStringBytes(JS_ValueToString(cx, argv[7])); + network_addr = JS_GetStringBytes(JS_ValueToString(cx, argv[7])); } if (argc > 8) { - ani2 = JS_GetStringBytes(JS_ValueToString(cx, argv[8])); + ani = JS_GetStringBytes(JS_ValueToString(cx, argv[8])); } if (argc > 9) { - rdnis = JS_GetStringBytes(JS_ValueToString(cx, argv[9])); + ani2 = JS_GetStringBytes(JS_ValueToString(cx, argv[9])); } + if (argc > 10) { + rdnis = JS_GetStringBytes(JS_ValueToString(cx, argv[10])); + } + if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n"); return JS_FALSE; } - caller_profile = switch_caller_profile_new(pool, dialplan, cid_name, cid_num, network_addr, ani, ani2, rdnis, (char *)modname, dest); + caller_profile = switch_caller_profile_new(pool, dialplan, cid_name, cid_num, network_addr, ani, ani2, rdnis, (char *)modname, context, dest); if (switch_core_session_outgoing_channel(session, channel_type, caller_profile, &peer_session, pool) == SWITCH_STATUS_SUCCESS) { jss = switch_core_session_alloc(peer_session, sizeof(*jss)); jss->session = peer_session; diff --git a/src/switch_caller.c b/src/switch_caller.c index beaa1b760d..c1ffd1b5c2 100644 --- a/src/switch_caller.c +++ b/src/switch_caller.c @@ -40,6 +40,7 @@ SWITCH_DECLARE(switch_caller_profile *) switch_caller_profile_new(switch_memory_ char *ani2, char *rdnis, char *source, + char *context, char *destination_number) { @@ -47,6 +48,9 @@ SWITCH_DECLARE(switch_caller_profile *) switch_caller_profile_new(switch_memory_ switch_caller_profile *profile = NULL; if ((profile = switch_core_alloc(pool, sizeof(switch_caller_profile))) != 0) { + if (!context) { + context = "default"; + } profile->dialplan = switch_core_strdup(pool, dialplan); profile->caller_id_name = switch_core_strdup(pool, caller_id_name); profile->caller_id_number = switch_core_strdup(pool, caller_id_number); @@ -55,6 +59,7 @@ SWITCH_DECLARE(switch_caller_profile *) switch_caller_profile_new(switch_memory_ profile->ani2 = switch_core_strdup(pool, ani2); profile->rdnis = switch_core_strdup(pool, rdnis); profile->source = switch_core_strdup(pool, source); + profile->context = switch_core_strdup(pool, context); profile->destination_number = switch_core_strdup(pool, destination_number); } @@ -77,6 +82,7 @@ SWITCH_DECLARE(switch_caller_profile *) switch_caller_profile_clone(switch_core_ profile->destination_number = switch_core_session_strdup(session, tocopy->destination_number); profile->uuid = switch_core_session_strdup(session, tocopy->uuid); profile->source = switch_core_session_strdup(session, tocopy->source); + profile->context = switch_core_session_strdup(session, tocopy->context); profile->chan_name = switch_core_session_strdup(session, tocopy->chan_name); } @@ -121,9 +127,13 @@ SWITCH_DECLARE(void) switch_caller_profile_event_set_data(switch_caller_profile switch_event_add_header(event, SWITCH_STACK_BOTTOM, header_name, caller_profile->uuid); } if (caller_profile->source) { - snprintf(header_name, sizeof(header_name), "%s-RDNIS", prefix); + snprintf(header_name, sizeof(header_name), "%s-Source", prefix); switch_event_add_header(event, SWITCH_STACK_BOTTOM, header_name, caller_profile->source); } + if (caller_profile->context) { + snprintf(header_name, sizeof(header_name), "%s-Context", prefix); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, header_name, caller_profile->context); + } if (caller_profile->rdnis) { snprintf(header_name, sizeof(header_name), "%s-RDNIS", prefix); switch_event_add_header(event, SWITCH_STACK_BOTTOM, header_name, caller_profile->rdnis); diff --git a/src/switch_channel.c b/src/switch_channel.c index ffca3d5af2..48460d1d9c 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -89,6 +89,7 @@ struct switch_channel { char *name; switch_buffer *dtmf_buffer; switch_mutex_t *dtmf_mutex; + switch_mutex_t *profile_mutex; switch_core_session *session; switch_channel_state state; uint32_t flags; @@ -99,7 +100,7 @@ struct switch_channel { const struct switch_state_handler_table *state_handlers[SWITCH_MAX_STATE_HANDLERS]; int state_handler_index; switch_hash *variables; - switch_channel_timetable_t times; + switch_channel_timetable_t *times; void *private_info; switch_call_cause_t hangup_cause; int freq; @@ -144,7 +145,7 @@ SWITCH_DECLARE(switch_call_cause_t) switch_channel_get_cause(switch_channel *cha SWITCH_DECLARE(switch_channel_timetable_t *) switch_channel_get_timetable(switch_channel *channel) { - return &channel->times; + return channel->times; } SWITCH_DECLARE(switch_status) switch_channel_alloc(switch_channel **channel, switch_memory_pool *pool) @@ -158,7 +159,7 @@ SWITCH_DECLARE(switch_status) switch_channel_alloc(switch_channel **channel, swi switch_core_hash_init(&(*channel)->variables, pool); switch_buffer_create(pool, &(*channel)->dtmf_buffer, 128); switch_mutex_init(&(*channel)->dtmf_mutex, SWITCH_MUTEX_NESTED, pool); - (*channel)->times.created = switch_time_now(); + switch_mutex_init(&(*channel)->profile_mutex, SWITCH_MUTEX_NESTED, pool); (*channel)->hangup_cause = SWITCH_CAUSE_UNALLOCATED; return SWITCH_STATUS_SUCCESS; @@ -297,8 +298,9 @@ SWITCH_DECLARE(switch_status) switch_channel_set_name(switch_channel *channel, c assert(channel != NULL); channel->name = NULL; if (name) { + char *uuid = switch_core_session_get_uuid(channel->session); channel->name = switch_core_session_strdup(channel->session, name); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "New Channel %s\n", name); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "New Chan %s [%s]\n", name, uuid); } return SWITCH_STATUS_SUCCESS; } @@ -354,6 +356,7 @@ static const char *state_names[] = { "CS_TRANSMIT", "CS_EXECUTE", "CS_LOOPBACK", + "CS_HOLD", "CS_HANGUP", "CS_DONE" }; @@ -418,6 +421,7 @@ SWITCH_DECLARE(switch_channel_state) switch_channel_perform_set_state(switch_cha case CS_TRANSMIT: case CS_RING: case CS_EXECUTE: + case CS_HOLD: ok++; default: break; @@ -429,6 +433,7 @@ SWITCH_DECLARE(switch_channel_state) switch_channel_perform_set_state(switch_cha case CS_TRANSMIT: case CS_RING: case CS_EXECUTE: + case CS_HOLD: ok++; default: break; @@ -440,6 +445,19 @@ SWITCH_DECLARE(switch_channel_state) switch_channel_perform_set_state(switch_cha case CS_LOOPBACK: case CS_RING: case CS_EXECUTE: + case CS_HOLD: + ok++; + default: + break; + } + break; + + case CS_HOLD: + switch (state) { + case CS_LOOPBACK: + case CS_RING: + case CS_EXECUTE: + case CS_TRANSMIT: ok++; default: break; @@ -447,10 +465,12 @@ SWITCH_DECLARE(switch_channel_state) switch_channel_perform_set_state(switch_cha break; case CS_RING: + switch_clear_flag(channel, CF_TRANSFER); switch (state) { case CS_LOOPBACK: case CS_EXECUTE: case CS_TRANSMIT: + case CS_HOLD: ok++; default: break; @@ -462,6 +482,7 @@ SWITCH_DECLARE(switch_channel_state) switch_channel_perform_set_state(switch_cha case CS_LOOPBACK: case CS_TRANSMIT: case CS_RING: + case CS_HOLD: ok++; default: break; @@ -569,8 +590,11 @@ SWITCH_DECLARE(void) switch_channel_event_set_data(switch_channel *channel, swit SWITCH_DECLARE(void) switch_channel_set_caller_profile(switch_channel *channel, switch_caller_profile *caller_profile) { + switch_channel_timetable_t *times; + assert(channel != NULL); assert(channel->session != NULL); + switch_mutex_lock(channel->profile_mutex); if (!caller_profile->uuid) { caller_profile->uuid = switch_core_session_strdup(channel->session, switch_core_session_get_uuid(channel->session)); @@ -587,35 +611,62 @@ SWITCH_DECLARE(void) switch_channel_set_caller_profile(switch_channel *channel, switch_channel_event_set_data(channel, event); switch_event_fire(&event); } + } + + if ((times = (switch_channel_timetable_t *) switch_core_session_alloc(channel->session, sizeof(*times)))) { + times->next = channel->times; + channel->times = times; } + channel->times->created = switch_time_now(); + caller_profile->next = channel->caller_profile; channel->caller_profile = caller_profile; + + switch_mutex_unlock(channel->profile_mutex); } SWITCH_DECLARE(switch_caller_profile *) switch_channel_get_caller_profile(switch_channel *channel) { + switch_caller_profile *profile; assert(channel != NULL); - return channel->caller_profile; + + switch_mutex_lock(channel->profile_mutex); + profile = channel->caller_profile; + switch_mutex_unlock(channel->profile_mutex); + + return profile; } SWITCH_DECLARE(void) switch_channel_set_originator_caller_profile(switch_channel *channel, switch_caller_profile *caller_profile) { assert(channel != NULL); + switch_mutex_lock(channel->profile_mutex); + caller_profile->next = channel->originator_caller_profile; channel->originator_caller_profile = caller_profile; + switch_mutex_unlock(channel->profile_mutex); } SWITCH_DECLARE(void) switch_channel_set_originatee_caller_profile(switch_channel *channel, switch_caller_profile *caller_profile) { assert(channel != NULL); + switch_mutex_lock(channel->profile_mutex); + caller_profile->next = channel->originatee_caller_profile; channel->originatee_caller_profile = caller_profile; + switch_mutex_unlock(channel->profile_mutex); } SWITCH_DECLARE(switch_caller_profile *) switch_channel_get_originator_caller_profile(switch_channel *channel) { + switch_caller_profile *profile; assert(channel != NULL); - return channel->originator_caller_profile; + + switch_mutex_lock(channel->profile_mutex); + profile = channel->originator_caller_profile; + switch_mutex_unlock(channel->profile_mutex); + + return profile; } SWITCH_DECLARE(char *) switch_channel_get_uuid(switch_channel *channel) @@ -627,8 +678,14 @@ SWITCH_DECLARE(char *) switch_channel_get_uuid(switch_channel *channel) SWITCH_DECLARE(switch_caller_profile *) switch_channel_get_originatee_caller_profile(switch_channel *channel) { + switch_caller_profile *profile; assert(channel != NULL); - return channel->originatee_caller_profile; + + switch_mutex_lock(channel->profile_mutex); + profile = channel->originatee_caller_profile; + switch_mutex_unlock(channel->profile_mutex); + + return profile; } SWITCH_DECLARE(int) switch_channel_add_state_handler(switch_channel *channel, @@ -704,8 +761,8 @@ SWITCH_DECLARE(switch_channel_state) switch_channel_perform_hangup(switch_channe { assert(channel != NULL); - if (!channel->times.hungup) { - channel->times.hungup = switch_time_now(); + if (!channel->times->hungup) { + channel->times->hungup = switch_time_now(); } if (channel->state < CS_HANGUP) { @@ -769,7 +826,7 @@ SWITCH_DECLARE(switch_status) switch_channel_perform_answer(switch_channel *chan if (switch_core_session_answer_channel(channel->session) == SWITCH_STATUS_SUCCESS) { switch_event *event; - channel->times.answered = switch_time_now(); + channel->times->answered = switch_time_now(); switch_channel_set_flag(channel, CF_ANSWERED); switch_log_printf(SWITCH_CHANNEL_ID_LOG, (char *) file, func, line, SWITCH_LOG_NOTICE, "Answer %s!\n", channel->name); if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_ANSWER) == SWITCH_STATUS_SUCCESS) { diff --git a/src/switch_core.c b/src/switch_core.c index f8721abf0a..480194b0b6 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -69,6 +69,8 @@ struct switch_core_session { switch_mutex_t *mutex; switch_thread_cond_t *cond; + switch_thread_rwlock_t *rwlock; + void *streams[SWITCH_MAX_STREAMS]; int stream_count; @@ -101,6 +103,7 @@ static void switch_core_standard_on_ring(switch_core_session *session); static void switch_core_standard_on_execute(switch_core_session *session); static void switch_core_standard_on_loopback(switch_core_session *session); static void switch_core_standard_on_transmit(switch_core_session *session); +static void switch_core_standard_on_hold(switch_core_session *session); /* The main runtime obj we keep this hidden for ourselves */ @@ -223,37 +226,70 @@ SWITCH_DECLARE(const switch_state_handler_table *) switch_core_get_state_handler return runtime.state_handlers[index]; } +SWITCH_DECLARE(switch_status) switch_core_session_read_lock(switch_core_session *session) +{ + return (switch_status) switch_thread_rwlock_tryrdlock(session->rwlock); +} + +SWITCH_DECLARE(void) switch_core_session_write_lock(switch_core_session *session) +{ + switch_thread_rwlock_wrlock(session->rwlock); +} + +SWITCH_DECLARE(void) switch_core_session_rwunlock(switch_core_session *session) +{ + switch_thread_rwlock_unlock(session->rwlock); +} + SWITCH_DECLARE(switch_core_session *) switch_core_session_locate(char *uuid_str) { switch_core_session *session; - session = switch_core_hash_find(runtime.session_table, uuid_str); + if ((session = switch_core_hash_find(runtime.session_table, uuid_str))) { + /* Acquire a read lock on the session */ + if (switch_thread_rwlock_tryrdlock(session->rwlock) != SWITCH_STATUS_SUCCESS) { + /* not available, forget it */ + session = NULL; + } + } + + /* if its not NULL, now it's up to you to rwunlock this */ return session; } SWITCH_DECLARE(switch_status) switch_core_session_message_send(char *uuid_str, switch_core_session_message *message) { switch_core_session *session = NULL; + switch_status status = SWITCH_STATUS_FALSE; if ((session = switch_core_hash_find(runtime.session_table, uuid_str)) != 0) { - if (switch_channel_get_state(session->channel) < CS_HANGUP) { - return switch_core_session_receive_message(session, message); + /* Acquire a read lock on the session or forget it the channel is dead */ + if (switch_thread_rwlock_tryrdlock(session->rwlock) == SWITCH_STATUS_SUCCESS) { + if (switch_channel_get_state(session->channel) < CS_HANGUP) { + status = switch_core_session_receive_message(session, message); + } + switch_thread_rwlock_unlock(session->rwlock); } } - return SWITCH_STATUS_FALSE; + return status; } SWITCH_DECLARE(switch_status) switch_core_session_event_send(char *uuid_str, switch_event *event) { switch_core_session *session = NULL; + switch_status status = SWITCH_STATUS_FALSE; if ((session = switch_core_hash_find(runtime.session_table, uuid_str)) != 0) { - if (switch_channel_get_state(session->channel) < CS_HANGUP) { - return switch_core_session_queue_event(session, event); + /* Acquire a read lock on the session or forget it the channel is dead */ + if (switch_thread_rwlock_tryrdlock(session->rwlock) == SWITCH_STATUS_SUCCESS) { + if (switch_channel_get_state(session->channel) < CS_HANGUP) { + status = switch_core_session_queue_event(session, event); + } + switch_thread_rwlock_unlock(session->rwlock); } } - return SWITCH_STATUS_FALSE; + return status; } SWITCH_DECLARE(char *) switch_core_session_get_uuid(switch_core_session *session) @@ -987,6 +1023,10 @@ SWITCH_DECLARE(switch_status) switch_core_session_read_frame(switch_core_session assert(session != NULL); *frame = NULL; + while (switch_channel_test_flag(session->channel, CF_HOLD)) { + return SWITCH_STATUS_BREAK; + } + if (session->endpoint_interface->io_routines->read_frame) { if ((status = session->endpoint_interface->io_routines->read_frame(session, frame, @@ -1175,6 +1215,10 @@ SWITCH_DECLARE(switch_status) switch_core_session_write_frame(switch_core_sessio assert(frame != NULL); assert(frame->codec != NULL); + if (switch_channel_test_flag(session->channel, CF_HOLD)) { + return SWITCH_STATUS_SUCCESS; + } + if (switch_test_flag(frame, SFF_CNG)) { if (switch_channel_test_flag(session->channel, CF_ACCEPT_CNG)) { @@ -1803,6 +1847,12 @@ static void switch_core_standard_on_transmit(switch_core_session *session) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Standard TRANSMIT\n"); } +static void switch_core_standard_on_hold(switch_core_session *session) +{ + assert(session != NULL); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Standard HOLD\n"); +} + SWITCH_DECLARE(void) switch_core_session_signal_state_change(switch_core_session *session) { switch_thread_cond_signal(session->cond); @@ -1855,7 +1905,7 @@ static int handle_fatality(int sig) print_trace(); longjmp(*env, sig); } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Caught SEGV for unmapped thread!"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Caught signal %d for unmapped thread!", sig); abort(); } @@ -2150,6 +2200,43 @@ SWITCH_DECLARE(void) switch_core_session_run(switch_core_session *session) } } break; + case CS_HOLD: /* wait in limbo */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) State HOLD\n", switch_channel_get_name(session->channel)); + if (!driver_state_handler->on_hold || + (driver_state_handler->on_hold && + driver_state_handler->on_hold(session) == SWITCH_STATUS_SUCCESS && + midstate == switch_channel_get_state(session->channel))) { + + while((application_state_handler = switch_channel_get_state_handler(session->channel, index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_hold || + (application_state_handler->on_hold && + application_state_handler->on_hold(session) == SWITCH_STATUS_SUCCESS && + midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + index = 0; + while(proceed && (application_state_handler = switch_core_get_state_handler(index++)) != 0) { + if (!application_state_handler || !application_state_handler->on_hold || + (application_state_handler->on_hold && + application_state_handler->on_hold(session) == SWITCH_STATUS_SUCCESS && + midstate == switch_channel_get_state(session->channel))) { + proceed++; + continue; + } else { + proceed = 0; + break; + } + } + if (proceed) { + switch_core_standard_on_hold(session); + } + } + break; } if (midstate == CS_DONE) { @@ -2282,13 +2369,11 @@ static void *SWITCH_THREAD_FUNC switch_core_session_thread(switch_thread *thread switch_core_hash_insert(runtime.session_table, session->uuid_str, session); switch_core_session_run(session); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Session %u (%s) Locked, Waiting on external entities\n", session->id, switch_channel_get_name(session->channel)); + switch_core_session_write_lock(session); + switch_core_session_rwunlock(session); - if (switch_channel_test_flag(session->channel, CF_LOCK_THREAD)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Session %u (%s) Locked\n", session->id, switch_channel_get_name(session->channel)); - while(switch_channel_test_flag(session->channel, CF_LOCK_THREAD)) { - switch_yield(10000); - } - } switch_core_hash_delete(runtime.session_table, session->uuid_str); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Session %u (%s) Ended\n", session->id, switch_channel_get_name(session->channel)); switch_core_session_destroy(&session); @@ -2393,6 +2478,7 @@ SWITCH_DECLARE(switch_core_session *) switch_core_session_request(const switch_e switch_mutex_init(&session->mutex, SWITCH_MUTEX_NESTED, session->pool); switch_thread_cond_create(&session->cond, session->pool); + switch_thread_rwlock_create(&session->rwlock, session->pool); return session; } diff --git a/src/switch_ivr.c b/src/switch_ivr.c index daa42c7fc6..90d65c4307 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -63,7 +63,9 @@ SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_callback(switch_core_ses break; } - if (switch_core_session_read_frame(session, &read_frame, -1, 0) != SWITCH_STATUS_SUCCESS) { + status = switch_core_session_read_frame(session, &read_frame, -1, 0); + + if (!SWITCH_READ_ACCEPTABLE(status)) { break; } } @@ -135,7 +137,9 @@ SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_count(switch_core_sessio } } if (poll_channel) { - if ((status = switch_core_session_read_frame(session, &read_frame, -1, 0)) != SWITCH_STATUS_SUCCESS) { + status = switch_core_session_read_frame(session, &read_frame, -1, 0); + + if (!SWITCH_READ_ACCEPTABLE(status)) { break; } } else { @@ -230,7 +234,8 @@ SWITCH_DECLARE(switch_status) switch_ivr_record_file(switch_core_session *sessio } } - if ((status = switch_core_session_read_frame(session, &read_frame, -1, 0)) != SWITCH_STATUS_SUCCESS) { + status = switch_core_session_read_frame(session, &read_frame, -1, 0); + if (!SWITCH_READ_ACCEPTABLE(status)) { break; } if (!switch_test_flag(fh, SWITCH_FILE_PAUSE)) { @@ -449,7 +454,12 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, } } else { /* time off the channel (if you must) */ switch_frame *read_frame; - if (switch_core_session_read_frame(session, &read_frame, -1, 0) != SWITCH_STATUS_SUCCESS) { + + while (switch_channel_test_flag(channel, CF_HOLD)) { + switch_yield(10000); + } + switch_status status = switch_core_session_read_frame(session, &read_frame, -1, 0); + if (!SWITCH_READ_ACCEPTABLE(status)) { break; } } @@ -655,7 +665,13 @@ SWITCH_DECLARE(switch_status) switch_ivr_speak_text(switch_core_session *session } } else { /* time off the channel (if you must) */ switch_frame *read_frame; - if (switch_core_session_read_frame(session, &read_frame, -1, 0) != SWITCH_STATUS_SUCCESS) { + switch_status status = switch_core_session_read_frame(session, &read_frame, -1, 0); + + while (switch_channel_test_flag(channel, CF_HOLD)) { + switch_yield(10000); + } + + if (!SWITCH_READ_ACCEPTABLE(status)) { break; } } @@ -718,53 +734,66 @@ static void *audio_bridge_thread(switch_thread *thread, void *obj) ans_a = switch_channel_test_flag(chan_a, CF_ANSWERED); ans_b = switch_channel_test_flag(chan_b, CF_ANSWERED); + switch_channel_set_flag(chan_a, CF_BRIDGED); while (data->running > 0 && his_thread->running > 0) { switch_channel_state b_state = switch_channel_get_state(chan_b); + switch_status status; switch (b_state) { case CS_HANGUP: + case CS_DONE: data->running = -1; continue; default: break; } - /* If this call is running on early media and it answers for real, pass it along... */ - if (!ans_b && switch_channel_test_flag(chan_a, CF_ANSWERED)) { - if (!switch_channel_test_flag(chan_b, CF_ANSWERED)) { - switch_channel_answer(chan_b); - } - ans_b++; + if (switch_channel_test_flag(chan_a, CF_TRANSFER)) { + break; } - if (!ans_a && switch_channel_test_flag(chan_b, CF_ANSWERED)) { - if (!switch_channel_test_flag(chan_a, CF_ANSWERED)) { - switch_channel_answer(chan_a); + if (!switch_channel_test_flag(chan_a, CF_HOLD)) { + /* If this call is running on early media and it answers for real, pass it along... */ + if (!ans_b && switch_channel_test_flag(chan_a, CF_ANSWERED)) { + if (!switch_channel_test_flag(chan_b, CF_ANSWERED)) { + switch_channel_answer(chan_b); + } + ans_b++; } - ans_a++; - } - /* if 1 channel has DTMF pass it to the other */ - if (switch_channel_has_dtmf(chan_a)) { - char dtmf[128]; - switch_channel_dequeue_dtmf(chan_a, dtmf, sizeof(dtmf)); - switch_core_session_send_dtmf(session_b, dtmf); + if (!ans_a && switch_channel_test_flag(chan_b, CF_ANSWERED)) { + if (!switch_channel_test_flag(chan_a, CF_ANSWERED)) { + switch_channel_answer(chan_a); + } + ans_a++; + } - if (dtmf_callback) { - if (dtmf_callback(session_a, dtmf, user_data, 0) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s ended call via DTMF\n", switch_channel_get_name(chan_a)); - data->running = -1; - break; + /* if 1 channel has DTMF pass it to the other */ + if (switch_channel_has_dtmf(chan_a)) { + char dtmf[128]; + switch_channel_dequeue_dtmf(chan_a, dtmf, sizeof(dtmf)); + switch_core_session_send_dtmf(session_b, dtmf); + + if (dtmf_callback) { + if (dtmf_callback(session_a, dtmf, user_data, 0) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s ended call via DTMF\n", switch_channel_get_name(chan_a)); + data->running = -1; + break; + } } } } /* read audio from 1 channel and write it to the other */ - if (switch_core_session_read_frame(session_a, &read_frame, -1, stream_id) == SWITCH_STATUS_SUCCESS && read_frame->datalen) { - if (switch_core_session_write_frame(session_b, read_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "write: %s Bad Frame....[%u] Bubye!\n", switch_channel_get_name(chan_b), read_frame->datalen); - data->running = -1; + status = switch_core_session_read_frame(session_a, &read_frame, -1, stream_id); + + if (SWITCH_READ_ACCEPTABLE(status)) { + if (status != SWITCH_STATUS_BREAK) { + if (switch_core_session_write_frame(session_b, read_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "write: %s Bad Frame....[%u] Bubye!\n", switch_channel_get_name(chan_b), read_frame->datalen); + data->running = -1; + } } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "read: %s Bad Frame.... Bubye!\n", switch_channel_get_name(chan_a)); @@ -783,13 +812,7 @@ static void *audio_bridge_thread(switch_thread *thread, void *obj) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "BRIDGE THREAD DONE [%s]\n", switch_channel_get_name(chan_a)); if (switch_channel_test_flag(chan_a, CF_ORIGINATOR)) { - if (switch_channel_test_flag(chan_b, CF_TRANSFER)) { - if (switch_channel_get_state(chan_b) < CS_HANGUP) { - switch_channel_set_state(chan_b, CS_RING); - /* TBD we need to teach all the endpoints to honor this still */ - switch_core_session_kill_channel(session_b, SWITCH_SIG_XFER); - } - } else { + if (!switch_channel_test_flag(chan_b, CF_TRANSFER)) { switch_core_session_kill_channel(session_b, SWITCH_SIG_KILL); switch_channel_hangup(chan_b, SWITCH_CAUSE_NORMAL_CLEARING); } @@ -797,7 +820,7 @@ static void *audio_bridge_thread(switch_thread *thread, void *obj) his_thread->running = 0; } - + switch_channel_clear_flag(chan_a, CF_BRIDGED); data->running = 0; return NULL; } @@ -832,18 +855,18 @@ static switch_status audio_bridge_on_ring(switch_core_session *session) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CUSTOM RING\n"); /* put the channel in a passive state so we can loop audio to it */ - switch_channel_set_state(channel, CS_TRANSMIT); + switch_channel_set_state(channel, CS_HOLD); return SWITCH_STATUS_FALSE; } -static switch_status audio_bridge_on_transmit(switch_core_session *session) +static switch_status audio_bridge_on_hold(switch_core_session *session) { switch_channel *channel = NULL; channel = switch_core_session_get_channel(session); assert(channel != NULL); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CUSTOM TRANSMIT\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CUSTOM HOLD\n"); /* put the channel in a passive state so we can loop audio to it */ return SWITCH_STATUS_FALSE; @@ -855,7 +878,8 @@ static const switch_state_handler_table audio_bridge_peer_state_handlers = { /*.on_execute */ NULL, /*.on_hangup */ NULL, /*.on_loopback */ audio_bridge_on_loopback, - /*.on_transmit */ audio_bridge_on_transmit, + /*.on_transmit */ NULL, + /*.on_hold */ audio_bridge_on_hold, }; @@ -874,7 +898,7 @@ SWITCH_DECLARE(switch_status) switch_ivr_multi_threaded_bridge(switch_core_sessi time_t start; int stream_id = 0; switch_frame *read_frame = NULL; - + switch_status status = SWITCH_STATUS_SUCCESS; caller_channel = switch_core_session_get_channel(session); @@ -941,7 +965,9 @@ SWITCH_DECLARE(switch_status) switch_ivr_multi_threaded_bridge(switch_core_sessi /* read from the channel while we wait if the audio is up on it */ if (switch_channel_test_flag(caller_channel, CF_ANSWERED) || switch_channel_test_flag(caller_channel, CF_EARLY_MEDIA)) { - if (switch_core_session_read_frame(session, &read_frame, 1000, 0) != SWITCH_STATUS_SUCCESS) { + switch_status status = switch_core_session_read_frame(session, &read_frame, 1000, 0); + + if (!SWITCH_READ_ACCEPTABLE(status)) { break; } if (read_frame) { @@ -964,8 +990,8 @@ SWITCH_DECLARE(switch_status) switch_ivr_multi_threaded_bridge(switch_core_sessi if (switch_channel_test_flag(peer_channel, CF_ANSWERED) || switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA)) { switch_event *event; switch_core_session_message msg = {0}; - - switch_channel_set_state(peer_channel, CS_TRANSMIT); + + switch_channel_set_state(peer_channel, CS_HOLD); if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_BRIDGE) == SWITCH_STATUS_SUCCESS) { switch_channel_event_set_data(caller_channel, event); @@ -980,31 +1006,79 @@ SWITCH_DECLARE(switch_status) switch_ivr_multi_threaded_bridge(switch_core_sessi msg.pointer_arg = peer_session; switch_core_session_receive_message(session, &msg); - switch_channel_set_flag(peer_channel, CF_LOCK_THREAD); - switch_channel_set_private(peer_channel, other_audio_thread); - switch_channel_set_state(peer_channel, CS_LOOPBACK); - audio_bridge_thread(NULL, (void *) this_audio_thread); + if (switch_core_session_read_lock(peer_session) == SWITCH_STATUS_SUCCESS) { + switch_channel_set_private(peer_channel, other_audio_thread); + switch_channel_set_state(peer_channel, CS_LOOPBACK); + audio_bridge_thread(NULL, (void *) this_audio_thread); - if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_UNBRIDGE) == SWITCH_STATUS_SUCCESS) { - switch_channel_event_set_data(caller_channel, event); - switch_event_fire(&event); - } + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_UNBRIDGE) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(caller_channel, event); + switch_event_fire(&event); + } - this_audio_thread->objs[0] = NULL; - this_audio_thread->objs[1] = NULL; - this_audio_thread->objs[2] = NULL; - this_audio_thread->objs[3] = NULL; - this_audio_thread->objs[4] = NULL; - this_audio_thread->objs[5] = NULL; - this_audio_thread->running = 2; - - switch_channel_clear_flag(peer_channel, CF_LOCK_THREAD); + this_audio_thread->objs[0] = NULL; + this_audio_thread->objs[1] = NULL; + this_audio_thread->objs[2] = NULL; + this_audio_thread->objs[3] = NULL; + this_audio_thread->objs[4] = NULL; + this_audio_thread->objs[5] = NULL; + this_audio_thread->running = 2; + 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_LOG, SWITCH_LOG_WARNING, "Bridge Failed %s->%s\n", switch_channel_get_name(caller_channel), switch_channel_get_name(peer_channel) ); - return SWITCH_STATUS_FALSE; } - return SWITCH_STATUS_SUCCESS; + + return status; } + + + +SWITCH_DECLARE(switch_status) switch_ivr_session_transfer(switch_core_session *session, char *extension, char *dialplan, char *context) +{ + switch_channel *channel; + switch_caller_profile *profile, *new_profile; + + assert(session != NULL); + assert(extension != NULL); + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + if ((profile = switch_channel_get_caller_profile(channel))) { + new_profile = switch_caller_profile_clone(session, profile); + new_profile->destination_number = switch_core_session_strdup(session, extension); + + if (dialplan) { + new_profile->dialplan = switch_core_session_strdup(session, dialplan); + } else { + dialplan = new_profile->dialplan; + } + + if (context) { + new_profile->context = switch_core_session_strdup(session, context); + } else { + context = new_profile->context; + } + + switch_channel_set_caller_profile(channel, new_profile); + switch_channel_set_flag(channel, CF_TRANSFER); + switch_channel_set_state(channel, CS_RING); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Transfer %s to %s[%s@%s]\n", + switch_channel_get_name(channel), dialplan, extension, context); + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; +} + +