]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
add transfer capability and small ways to test it
authorAnthony Minessale <anthony.minessale@gmail.com>
Fri, 28 Apr 2006 19:46:57 +0000 (19:46 +0000)
committerAnthony Minessale <anthony.minessale@gmail.com>
Fri, 28 Apr 2006 19:46:57 +0000 (19:46 +0000)
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@1290 d0543943-73ff-0310-b7d9-9358b9ac24b2

23 files changed:
src/include/switch_apr.h
src/include/switch_caller.h
src/include/switch_channel.h
src/include/switch_core.h
src/include/switch_ivr.h
src/include/switch_module_interfaces.h
src/include/switch_types.h
src/include/switch_utils.h
src/mod/applications/mod_bridgecall/mod_bridgecall.c
src/mod/applications/mod_commands/mod_commands.c
src/mod/dialplans/mod_pcre/mod_pcre.c
src/mod/endpoints/mod_dingaling/mod_dingaling.c
src/mod/endpoints/mod_exosip/mod_exosip.c
src/mod/endpoints/mod_iax/mod_iax.c
src/mod/endpoints/mod_portaudio/mod_portaudio.c
src/mod/endpoints/mod_wanpipe/mod_wanpipe.c
src/mod/endpoints/mod_woomera/mod_woomera.c
src/mod/event_handlers/mod_event_test/mod_event_test.c
src/mod/languages/mod_spidermonkey/mod_spidermonkey.c
src/switch_caller.c
src/switch_channel.c
src/switch_core.c
src/switch_ivr.c

index cb4c2a2a49470e49af03e0725096eb793f2bd502..ffdf3a0bd54da6d5959211299a640babc4838257 100644 (file)
@@ -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
index 5d2809df93d362548245c483377a688495f138e9..7b09ec64625dc8f45cc7dac3a095ccf623ce3f4c 100644 (file)
@@ -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);
 
 /*!
index 9cba98e08051c537eda6e596db356c0d99c2cdf3..34c805c27c5119c629ab098681b6583f3e867601 100644 (file)
 #ifdef __cplusplus
 extern "C" {
 #endif
+#ifdef __FORMATBUG
+}
+#endif
 
 #include <switch.h>
 
-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
index acd650a79257ecf442834ade84ae3f8171e10292..9366e54d84461b33e95691f9b3bcc390aa22794d 100644 (file)
@@ -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);
 
index ced4ad0c26221b08a01db8a28ba74785c77a9b60..a3e474b993a906481142e12ee3cf620d6fd8fd4f 100644 (file)
@@ -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
index f9b8e34a3a1c597e0d7c2f6c995ff5406ab6440d..74a766d63540aa41df8bd3cae56b5bbd505dd353 100644 (file)
@@ -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 */
index 0dab55dc16252b37f3fc89b4660abf925b63df44..b6c36cd74ccd30f0cb7cd7528211f9b98fc864a5 100644 (file)
@@ -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
 </pre>
@@ -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
 </pre>
  */
 
@@ -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;
 
 
index bbcb85486ba587c1e22a727ab8608dd2e01f64fa..26629d93ac8847722f73c6dc1890c6bb3cc597e7 100644 (file)
@@ -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
index 0bddb79ea96fdd7a17d410f1c38e71a5bdc0c61b..0e1fcbcc903b40f9c061c3ab3cf612d8b69bcf89 100644 (file)
@@ -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);
 
 
index 3c32bc9e1638c30d45dd0915f35a1d94902a31a8..a5c53fd7d98832c275c9109d5ebe823fb34564ff 100644 (file)
@@ -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
 };
 
 
index c760c51edc0df2b666b1b359c5430dee050c6845..a584ed41ed601a5b464a639b004ff760f703635b 100644 (file)
@@ -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;
+                               }
+
                        }
                }
 
index 346dcd6cdd1a6cf9f2b67c0432c425b78e98c05b..edfc951607911f51e22255cb39236e19826fc51c 100644 (file)
@@ -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,
index 07d77f2a3800f3ef1b3c641485a1c01bb10bde7e..9394849247002bdb31a5cd1f31fa7c7f6484708d 100644 (file)
@@ -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);
                }
index 828caa40113e8ba2ca81b0e0b496619af366dd76..a34e7a110060a48fbd7c38d961f3fe4a60b5f482 100644 (file)
@@ -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,
index c503122d43859e15840ab22a0181dd10f47b4360..da348c1d6b9683a96aab806ac69e92be8554fa20 100644 (file)
@@ -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->
index 53aa5b7f1ecb2321c25422f9244ce40edc8b1238..2484198483cd51d5e5ef08d1b603f4f247e6ae71 100644 (file)
@@ -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);
                }
index 758e5f7ca15a709ecfffe4de07d9f853f6088683..c95731a5a222b966ed528b4960257bb3e9256080 100644 (file)
@@ -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);
index afc7bcf4c722322e4a71cf5f357b494e154f224a..62646ed425beb903ee4006319537c1ddfd1002cc 100644 (file)
@@ -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;
        }
 }
index 9cda046007af12833d98b369203da0f784788f25..4cc582cf1862ca4f72ad22d20b2f7af47f206dc5 100644 (file)
@@ -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;
index beaa1b760d5cbd5ef1a86fe5750fbb2eac6bd098..c1ffd1b5c2f6983174cb5e3dafe09742eba09af5 100644 (file)
@@ -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);
index ffca3d5af2d8e6b5b1cc8230e57a3c4e3cac00fc..48460d1d9c264c5ab7d45001139b8a8cb7ffb56f 100644 (file)
@@ -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) {
index f8721abf0a917316734d51317b740c87b3404138..480194b0b65b82138657e665cdae91b9084408b0 100644 (file)
@@ -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;
 }
index daa42c7fc62a8fec82b910b36cf4d5b508feeb53..90d65c43078acc6dd32262bc28e56c8a84c2fc61 100644 (file)
@@ -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;
 }
+
+