Index: res_features.c =================================================================== RCS file: /usr/cvsroot/asterisk/res/res_features.c,v retrieving revision 1.73 diff -u -r1.73 res_features.c --- res_features.c 28 Sep 2005 23:10:14 -0000 1.73 +++ res_features.c 12 Oct 2005 17:38:23 -0000 @@ -1820,6 +1820,173 @@ return RESULT_SUCCESS; } +static char mandescr_bridge[] = +"Description: Bridge two active channels together.\n" +"Variables: (Names marked with * are required)\n" +" *Channel1: Channel to Bridge to Channel2\n" +" *Channel2: Channel to Bridge to Channel1.\n" +" Tone: (Yes|No) Play Courtesy tone to Channel2.\n" +"\n"; +static int action_bridge(struct mansession *s, struct message *m) +{ + char *channela = astman_get_header(m, "Channel1"); + char *channelb = astman_get_header(m, "Channel2"); + char *pt = astman_get_header(m, "Tone"); + char buf[BUFSIZ]; + struct ast_channel *chana = NULL, *chanb = NULL; + struct ast_channel *tmpchana = ast_channel_alloc(0), *tmpchanb = ast_channel_alloc(0); + struct ast_bridge_thread_obj *tobj = NULL; + int res = 0; + int playtone = 0; + + /* Find out if we're supposed to play a tone or not */ + playtone = ast_true(pt); + + /* Make sure we got input for both channels then find + * their actual channel pointers + */ + if (!ast_strlen_zero(channela) && !ast_strlen_zero(channelb)) { + chana = ast_get_channel_by_name_locked(channela); + chanb = ast_get_channel_by_name_locked(channelb); + if (chana) + ast_mutex_unlock(&chana->lock); + if (chanb) + ast_mutex_unlock(&chanb->lock); + + /* Make sure the pointers exist - err if not */ + if (!chana) { + snprintf(buf, sizeof(buf), "Channel does not exist: %s", channela); + astman_send_error(s, m, buf); + return 0; + } + if (!chanb) { + snprintf(buf, sizeof(buf), "Channel does not exist: %s", channelb); + astman_send_error(s, m, buf); + return 0; + } + } else { + snprintf(buf, sizeof(buf), "Missing parameter"); + astman_send_error(s, m, buf); + return 0; + } + + if (chana->_state != AST_STATE_UP) { + ast_answer(chana); + } + if (chanb->_state != AST_STATE_UP) { + ast_answer(chanb); + } + + if (tmpchana) { + /* Rename the Bridge channels so that we can safely remove them after masqerading */ + snprintf(tmpchana->name, sizeof(tmpchana->name), "Bridge/%s", chana->name); + ast_moh_stop(chana); + ast_mutex_lock(&chana->lock); + ast_setstate(tmpchana, chana->_state); + tmpchana->readformat = chana->readformat; + tmpchana->writeformat = chana->writeformat; + ast_channel_masquerade(tmpchana, chana); + ast_mutex_lock(&tmpchana->lock); + ast_do_masquerade(tmpchana); + ast_mutex_unlock(&tmpchana->lock); + ast_mutex_unlock(&chana->lock); + } else { + snprintf(buf, sizeof(buf), "Unable to create temporary channels!"); + astman_send_error(s, m, buf); + ast_hangup(tmpchana); + ast_hangup(chana); + return 1; + } + if (tmpchanb) { + /* Rename the Bridge channels so that we can safely remove them after masqerading */ + snprintf(tmpchanb->name, sizeof(tmpchanb->name), "Bridge/%s", chanb->name); + ast_moh_stop(chanb); + ast_mutex_lock(&chanb->lock); + ast_setstate(tmpchanb, chanb->_state); + tmpchanb->readformat = chanb->readformat; + tmpchanb->writeformat = chanb->writeformat; + ast_channel_masquerade(tmpchanb, chanb); + ast_mutex_lock(&tmpchanb->lock); + ast_do_masquerade(tmpchanb); + ast_mutex_unlock(&tmpchanb->lock); + ast_mutex_unlock(&chanb->lock); + } else { + snprintf(buf, sizeof(buf), "Unable to create temporary channels!"); + astman_send_error(s, m, buf); + ast_hangup(tmpchanb); + ast_hangup(chanb); + return 1; + } + + /* Make the channels compatible - err if we fail in doing so.*/ + res = ast_channel_make_compatible(tmpchana, tmpchanb); + if (res) { + ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chana->name, chanb->name); + ast_hangup(tmpchana); + ast_hangup(tmpchanb); + ast_cli(s->fd, "Event: Bridge\r\n" + "Response: Failed\r\n" + "Reason: Could not make channels compatible\r\n" + "Channel1: %s\r\n" + "Channel2: %s\r\n" + "Tone: %s\r\n" + "\r\n", + channela, channelb, playtone ? "Yes" : "No"); + return 1; + } else { + /* Report thate the bridge will be successful */ + ast_cli(s->fd, "Event: Bridge\r\n" + "Response: Success\r\n" + "Channel1: %s\r\n" + "Channel2: %s\r\n" + "Tone: %s\r\n" + "\r\n", + channela, channelb, playtone ? "Yes" : "No"); + } + + /* Set up the bridge thread object, and start the bridge */ + tobj = malloc(sizeof(struct ast_bridge_thread_obj)); + if (tobj) { + memset(tobj, 0, sizeof(struct ast_bridge_thread_obj)); + tobj->chan = tmpchana; + tobj->peer = tmpchanb; + tobj->bconfig.play_warning = 0; + tobj->bconfig.warning_freq = 0; + tobj->bconfig.warning_sound = NULL; + tobj->bconfig.end_sound = NULL; + tobj->bconfig.start_sound = NULL; + tobj->bconfig.firstpass = 0; + tobj->bconfig.timelimit = 0; + + if (playtone) { + if (!ast_strlen_zero(xfersound) && !ast_streamfile(tmpchanb, xfersound, tmpchanb->language)) { + if (ast_waitstream(tmpchanb, "") < 0) + ast_log(LOG_WARNING, "Failed to play courtesy tone!\n"); + } + } + ast_bridge_call_thread_launch(tobj); + } else { + ast_log(LOG_WARNING, "Unable to spawn new bridge on %s and %s: %s\n", tmpchana->name, tmpchanb->name, strerror(errno)); + ast_hangup(tmpchana); + ast_hangup(tmpchanb); + ast_hangup(chana); + ast_hangup(chanb); + return 1; + } + + /* Unlock the channels and end this action */ + if (tmpchana) + ast_mutex_unlock(&tmpchana->lock); + if (tmpchanb) + ast_mutex_unlock(&tmpchanb->lock); + if (chana) + ast_mutex_unlock(&chana->lock); + if (chanb) + ast_mutex_unlock(&chanb->lock); + + return 0; +} + static char showfeatures_help[] = "Usage: show features\n" " Lists currently configured features.\n"; @@ -2116,6 +2283,7 @@ res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2); if (!res) { ast_manager_register("ParkedCalls", 0, manager_parking_status, "List parked calls" ); + ast_manager_register2("Bridge", EVENT_FLAG_COMMAND, action_bridge, "Bridge two live channels", mandescr_bridge ); } return res; } @@ -2126,6 +2294,7 @@ STANDARD_HANGUP_LOCALUSERS; ast_manager_unregister("ParkedCalls"); + ast_manager_unregister("Bridge"); ast_cli_unregister(&showfeatures); ast_cli_unregister(&showparked); ast_unregister_application(parkcall);