summaryrefslogtreecommitdiff
path: root/channels
diff options
context:
space:
mode:
authorRichard Mudgett <rmudgett@digium.com>2013-05-21 18:00:22 +0000
committerRichard Mudgett <rmudgett@digium.com>2013-05-21 18:00:22 +0000
commit3d63833bd6c869b7efa383e8dea14be1a6eff998 (patch)
tree34957dd051b8f67c7cc58a510e24ee3873a61ad4 /channels
parente1e1cc2deefb92f8b43825f1f34e619354737842 (diff)
Merge in the bridge_construction branch to make the system use the Bridging API.
Breaks many things until they can be reworked. A partial list: chan_agent chan_dahdi, chan_misdn, chan_iax2 native bridging app_queue COLP updates DTMF attended transfers Protocol attended transfers git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@389378 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'channels')
-rw-r--r--channels/chan_agent.c4
-rw-r--r--channels/chan_bridge.c235
-rw-r--r--channels/chan_dahdi.c1
-rw-r--r--channels/chan_gulp.c1
-rw-r--r--channels/chan_h323.c1
-rw-r--r--channels/chan_iax2.c172
-rw-r--r--channels/chan_jingle.c1
-rw-r--r--channels/chan_local.c1452
-rw-r--r--channels/chan_mgcp.c102
-rw-r--r--channels/chan_misdn.c1
-rw-r--r--channels/chan_motif.c1
-rw-r--r--channels/chan_sip.c539
-rw-r--r--channels/chan_skinny.c1
-rw-r--r--channels/chan_unistim.c11
-rw-r--r--channels/chan_vpb.cc8
15 files changed, 177 insertions, 2353 deletions
diff --git a/channels/chan_agent.c b/channels/chan_agent.c
index 3fb6891cc..d72254ee7 100644
--- a/channels/chan_agent.c
+++ b/channels/chan_agent.c
@@ -31,7 +31,6 @@
* \ingroup channel_drivers
*/
/*** MODULEINFO
- <depend>chan_local</depend>
<depend>res_monitor</depend>
<support_level>core</support_level>
***/
@@ -346,6 +345,7 @@ static char *complete_agent_logoff_cmd(const char *line, const char *word, int p
static struct ast_channel* agent_get_base_channel(struct ast_channel *chan);
static int agent_logoff(const char *agent, int soft);
+/* BUGBUG This channel driver is totally hosed until it is rewritten. */
/*! \brief Channel interface description for PBX integration */
static struct ast_channel_tech agent_tech = {
.type = "Agent",
@@ -2589,5 +2589,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Agent Proxy Channel",
.unload = unload_module,
.reload = reload,
.load_pri = AST_MODPRI_CHANNEL_DRIVER,
- .nonoptreq = "res_monitor,chan_local",
+ .nonoptreq = "res_monitor",
);
diff --git a/channels/chan_bridge.c b/channels/chan_bridge.c
deleted file mode 100644
index 8eac76a82..000000000
--- a/channels/chan_bridge.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2008, Digium, Inc.
- *
- * Joshua Colp <jcolp@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \author Joshua Colp <jcolp@digium.com>
- *
- * \brief Bridge Interaction Channel
- *
- * \ingroup channel_drivers
- */
-
-/*** MODULEINFO
- <support_level>core</support_level>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <fcntl.h>
-#include <sys/signal.h>
-
-#include "asterisk/lock.h"
-#include "asterisk/channel.h"
-#include "asterisk/config.h"
-#include "asterisk/module.h"
-#include "asterisk/pbx.h"
-#include "asterisk/sched.h"
-#include "asterisk/io.h"
-#include "asterisk/acl.h"
-#include "asterisk/callerid.h"
-#include "asterisk/file.h"
-#include "asterisk/cli.h"
-#include "asterisk/app.h"
-#include "asterisk/bridging.h"
-#include "asterisk/astobj2.h"
-
-static struct ast_channel *bridge_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause);
-static int bridge_call(struct ast_channel *ast, const char *dest, int timeout);
-static int bridge_hangup(struct ast_channel *ast);
-static struct ast_frame *bridge_read(struct ast_channel *ast);
-static int bridge_write(struct ast_channel *ast, struct ast_frame *f);
-static struct ast_channel *bridge_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
-
-static struct ast_channel_tech bridge_tech = {
- .type = "Bridge",
- .description = "Bridge Interaction Channel",
- .requester = bridge_request,
- .call = bridge_call,
- .hangup = bridge_hangup,
- .read = bridge_read,
- .write = bridge_write,
- .write_video = bridge_write,
- .exception = bridge_read,
- .bridged_channel = bridge_bridgedchannel,
-};
-
-struct bridge_pvt {
- struct ast_channel *input; /*!< Input channel - talking to source */
- struct ast_channel *output; /*!< Output channel - talking to bridge */
-};
-
-/*! \brief Called when the user of this channel wants to get the actual channel in the bridge */
-static struct ast_channel *bridge_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
-{
- struct bridge_pvt *p = ast_channel_tech_pvt(chan);
- return (chan == p->input) ? p->output : bridge;
-}
-
-/*! \brief Called when a frame should be read from the channel */
-static struct ast_frame *bridge_read(struct ast_channel *ast)
-{
- return &ast_null_frame;
-}
-
-/*! \brief Called when a frame should be written out to a channel */
-static int bridge_write(struct ast_channel *ast, struct ast_frame *f)
-{
- struct bridge_pvt *p = ast_channel_tech_pvt(ast);
- struct ast_channel *other = NULL;
-
- ao2_lock(p);
- /* only write frames to output. */
- if (p->input == ast) {
- other = p->output;
- if (other) {
- ast_channel_ref(other);
- }
- }
- ao2_unlock(p);
-
- if (other) {
- ast_channel_unlock(ast);
- ast_queue_frame(other, f);
- ast_channel_lock(ast);
- other = ast_channel_unref(other);
- }
-
- return 0;
-}
-
-/*! \brief Called when the channel should actually be dialed */
-static int bridge_call(struct ast_channel *ast, const char *dest, int timeout)
-{
- struct bridge_pvt *p = ast_channel_tech_pvt(ast);
-
- /* If no bridge has been provided on the input channel, bail out */
- if (!ast_channel_internal_bridge(ast)) {
- return -1;
- }
-
- /* Impart the output channel upon the given bridge of the input channel */
- return ast_bridge_impart(ast_channel_internal_bridge(p->input), p->output, NULL, NULL, 0)
- ? -1 : 0;
-}
-
-/*! \brief Called when a channel should be hung up */
-static int bridge_hangup(struct ast_channel *ast)
-{
- struct bridge_pvt *p = ast_channel_tech_pvt(ast);
-
- if (!p) {
- return 0;
- }
-
- ao2_lock(p);
- if (p->input == ast) {
- p->input = NULL;
- } else if (p->output == ast) {
- p->output = NULL;
- }
- ao2_unlock(p);
-
- ast_channel_tech_pvt_set(ast, NULL);
- ao2_ref(p, -1);
-
- return 0;
-}
-
-/*! \brief Called when we want to place a call somewhere, but not actually call it... yet */
-static struct ast_channel *bridge_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause)
-{
- struct bridge_pvt *p = NULL;
- struct ast_format slin;
-
- /* Try to allocate memory for our very minimal pvt structure */
- if (!(p = ao2_alloc(sizeof(*p), NULL))) {
- return NULL;
- }
-
- /* Try to grab two Asterisk channels to use as input and output channels */
- if (!(p->input = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", requestor ? ast_channel_linkedid(requestor) : NULL, 0, "Bridge/%p-input", p))) {
- ao2_ref(p, -1);
- return NULL;
- }
- if (!(p->output = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", requestor ? ast_channel_linkedid(requestor) : NULL, 0, "Bridge/%p-output", p))) {
- p->input = ast_channel_release(p->input);
- ao2_ref(p, -1);
- return NULL;
- }
-
- /* Setup parameters on both new channels */
- ast_channel_tech_set(p->input, &bridge_tech);
- ast_channel_tech_set(p->output, &bridge_tech);
-
- ao2_ref(p, 2);
- ast_channel_tech_pvt_set(p->input, p);
- ast_channel_tech_pvt_set(p->output, p);
-
- ast_format_set(&slin, AST_FORMAT_SLINEAR, 0);
-
- ast_format_cap_add(ast_channel_nativeformats(p->input), &slin);
- ast_format_cap_add(ast_channel_nativeformats(p->output), &slin);
- ast_format_copy(ast_channel_readformat(p->input), &slin);
- ast_format_copy(ast_channel_readformat(p->output), &slin);
- ast_format_copy(ast_channel_rawreadformat(p->input), &slin);
- ast_format_copy(ast_channel_rawreadformat(p->output), &slin);
- ast_format_copy(ast_channel_writeformat(p->input), &slin);
- ast_format_copy(ast_channel_writeformat(p->output), &slin);
- ast_format_copy(ast_channel_rawwriteformat(p->input), &slin);
- ast_format_copy(ast_channel_rawwriteformat(p->output), &slin);
-
- ast_answer(p->output);
- ast_answer(p->input);
-
- /* remove the reference from the alloc. The channels now own the pvt. */
- ao2_ref(p, -1);
- return p->input;
-}
-
-/*! \brief Load module into PBX, register channel */
-static int load_module(void)
-{
- if (!(bridge_tech.capabilities = ast_format_cap_alloc())) {
- return AST_MODULE_LOAD_FAILURE;
- }
-
- ast_format_cap_add_all(bridge_tech.capabilities);
- /* Make sure we can register our channel type */
- if (ast_channel_register(&bridge_tech)) {
- ast_log(LOG_ERROR, "Unable to register channel class 'Bridge'\n");
- return AST_MODULE_LOAD_FAILURE;
- }
- return AST_MODULE_LOAD_SUCCESS;
-}
-
-/*! \brief Unload the bridge interaction channel from Asterisk */
-static int unload_module(void)
-{
- ast_channel_unregister(&bridge_tech);
- bridge_tech.capabilities = ast_format_cap_destroy(bridge_tech.capabilities);
- return 0;
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Bridge Interaction Channel",
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER,
-);
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index 44fe3f4fb..1f9ee5a56 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -1553,6 +1553,7 @@ static int dahdi_func_write(struct ast_channel *chan, const char *function, char
static int dahdi_devicestate(const char *data);
static int dahdi_cc_callback(struct ast_channel *inbound, const char *dest, ast_cc_callback_fn callback);
+/* BUGBUG The DAHDI channel driver needs its own native bridge technology. */
static struct ast_channel_tech dahdi_tech = {
.type = "DAHDI",
.description = tdesc,
diff --git a/channels/chan_gulp.c b/channels/chan_gulp.c
index 74859534b..4af137f28 100644
--- a/channels/chan_gulp.c
+++ b/channels/chan_gulp.c
@@ -134,7 +134,6 @@ static struct ast_channel_tech gulp_tech = {
.send_text = gulp_sendtext,
.send_digit_begin = gulp_digit_begin,
.send_digit_end = gulp_digit_end,
- .bridge = ast_rtp_instance_bridge,
.call = gulp_call,
.hangup = gulp_hangup,
.answer = gulp_answer,
diff --git a/channels/chan_h323.c b/channels/chan_h323.c
index f6364a118..2476f5065 100644
--- a/channels/chan_h323.c
+++ b/channels/chan_h323.c
@@ -275,7 +275,6 @@ static struct ast_channel_tech oh323_tech = {
.write = oh323_write,
.indicate = oh323_indicate,
.fixup = oh323_fixup,
- .bridge = ast_rtp_instance_bridge,
};
static const char* redirectingreason2str(int redirectingreason)
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index 112a99375..49ce66cb7 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -102,6 +102,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/data.h"
#include "asterisk/netsock2.h"
#include "asterisk/security_events.h"
+#include "asterisk/bridging.h"
#include "iax2/include/iax2.h"
#include "iax2/include/firmware.h"
@@ -1258,6 +1259,7 @@ static void sched_delay_remove(struct sockaddr_in *sin, callno_entry entry);
static void network_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *message);
static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *message);
+/* BUGBUG The IAX2 channel driver needs its own native bridge technology. */
static struct ast_channel_tech iax2_tech = {
.type = "IAX2",
.description = tdesc,
@@ -9221,130 +9223,6 @@ static void spawn_dp_lookup(int callno, const char *context, const char *calledn
}
}
-struct iax_dual {
- struct ast_channel *chan1;
- struct ast_channel *chan2;
- char *park_exten;
- char *park_context;
-};
-
-static void *iax_park_thread(void *stuff)
-{
- struct iax_dual *d;
- int res;
- int ext = 0;
-
- d = stuff;
-
- ast_debug(4, "IAX Park: Transferer channel %s, Transferee %s\n",
- ast_channel_name(d->chan2), ast_channel_name(d->chan1));
-
- res = ast_park_call_exten(d->chan1, d->chan2, d->park_exten, d->park_context, 0, &ext);
- if (res) {
- /* Parking failed. */
- ast_hangup(d->chan1);
- } else {
- ast_log(LOG_NOTICE, "Parked on extension '%d'\n", ext);
- }
- ast_hangup(d->chan2);
-
- ast_free(d->park_exten);
- ast_free(d->park_context);
- ast_free(d);
- return NULL;
-}
-
-/*! DO NOT hold any locks while calling iax_park */
-static int iax_park(struct ast_channel *chan1, struct ast_channel *chan2, const char *park_exten, const char *park_context)
-{
- struct iax_dual *d;
- struct ast_channel *chan1m, *chan2m;/* Chan2m: The transferer, chan1m: The transferee */
- pthread_t th;
-
- chan1m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, ast_channel_accountcode(chan2), ast_channel_exten(chan1), ast_channel_context(chan1), ast_channel_linkedid(chan1), ast_channel_amaflags(chan1), "Parking/%s", ast_channel_name(chan1));
- chan2m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, ast_channel_accountcode(chan2), ast_channel_exten(chan2), ast_channel_context(chan2), ast_channel_linkedid(chan2), ast_channel_amaflags(chan2), "IAXPeer/%s", ast_channel_name(chan2));
- d = ast_calloc(1, sizeof(*d));
- if (!chan1m || !chan2m || !d) {
- if (chan1m) {
- ast_hangup(chan1m);
- }
- if (chan2m) {
- ast_hangup(chan2m);
- }
- ast_free(d);
- return -1;
- }
- d->park_exten = ast_strdup(park_exten);
- d->park_context = ast_strdup(park_context);
- if (!d->park_exten || !d->park_context) {
- ast_hangup(chan1m);
- ast_hangup(chan2m);
- ast_free(d->park_exten);
- ast_free(d->park_context);
- ast_free(d);
- return -1;
- }
-
- /* Make formats okay */
- ast_format_copy(ast_channel_readformat(chan1m), ast_channel_readformat(chan1));
- ast_format_copy(ast_channel_writeformat(chan1m), ast_channel_writeformat(chan1));
-
- /* Prepare for taking over the channel */
- if (ast_channel_masquerade(chan1m, chan1)) {
- ast_hangup(chan1m);
- ast_hangup(chan2m);
- ast_free(d->park_exten);
- ast_free(d->park_context);
- ast_free(d);
- return -1;
- }
-
- /* Setup the extensions and such */
- ast_channel_context_set(chan1m, ast_channel_context(chan1));
- ast_channel_exten_set(chan1m, ast_channel_exten(chan1));
- ast_channel_priority_set(chan1m, ast_channel_priority(chan1));
-
- ast_do_masquerade(chan1m);
-
- /* We make a clone of the peer channel too, so we can play
- back the announcement */
-
- /* Make formats okay */
- ast_format_copy(ast_channel_readformat(chan2m), ast_channel_readformat(chan2));
- ast_format_copy(ast_channel_writeformat(chan2m), ast_channel_writeformat(chan2));
- ast_channel_parkinglot_set(chan2m, ast_channel_parkinglot(chan2));
-
- /* Prepare for taking over the channel */
- if (ast_channel_masquerade(chan2m, chan2)) {
- ast_hangup(chan1m);
- ast_hangup(chan2m);
- ast_free(d->park_exten);
- ast_free(d->park_context);
- ast_free(d);
- return -1;
- }
-
- /* Setup the extensions and such */
- ast_channel_context_set(chan2m, ast_channel_context(chan2));
- ast_channel_exten_set(chan2m, ast_channel_exten(chan2));
- ast_channel_priority_set(chan2m, ast_channel_priority(chan2));
-
- ast_do_masquerade(chan2m);
-
- d->chan1 = chan1m; /* Transferee */
- d->chan2 = chan2m; /* Transferer */
- if (ast_pthread_create_detached_background(&th, NULL, iax_park_thread, d) < 0) {
- /* Could not start thread */
- ast_hangup(chan1m);
- ast_hangup(chan2m);
- ast_free(d->park_exten);
- ast_free(d->park_context);
- ast_free(d);
- return -1;
- }
- return 0;
-}
-
static int check_provisioning(struct sockaddr_in *sin, int sockfd, char *si, unsigned int ver)
{
unsigned int ourver;
@@ -10774,56 +10652,28 @@ static int socket_process_helper(struct iax2_thread *thread)
break;
case IAX_COMMAND_TRANSFER:
{
- struct ast_channel *bridged_chan;
- struct ast_channel *owner;
-
iax2_lock_owner(fr->callno);
if (!iaxs[fr->callno]) {
/* Initiating call went away before we could transfer. */
break;
}
- owner = iaxs[fr->callno]->owner;
- bridged_chan = owner ? ast_bridged_channel(owner) : NULL;
- if (bridged_chan && ies.called_number) {
- const char *context;
-
- context = ast_strdupa(iaxs[fr->callno]->context);
+ if (iaxs[fr->callno]->owner) {
+ struct ast_channel *owner = iaxs[fr->callno]->owner;
+ char *context = ast_strdupa(iaxs[fr->callno]->context);
ast_channel_ref(owner);
- ast_channel_ref(bridged_chan);
ast_channel_unlock(owner);
ast_mutex_unlock(&iaxsl[fr->callno]);
- /* Set BLINDTRANSFER channel variables */
- pbx_builtin_setvar_helper(owner, "BLINDTRANSFER", ast_channel_name(bridged_chan));
- pbx_builtin_setvar_helper(bridged_chan, "BLINDTRANSFER", ast_channel_name(owner));
-
- /* DO NOT hold any locks while calling ast_parking_ext_valid() */
- if (ast_parking_ext_valid(ies.called_number, owner, context)) {
- ast_debug(1, "Parking call '%s'\n", ast_channel_name(bridged_chan));
- if (iax_park(bridged_chan, owner, ies.called_number, context)) {
- ast_log(LOG_WARNING, "Failed to park call '%s'\n",
- ast_channel_name(bridged_chan));
- }
- } else {
- if (ast_async_goto(bridged_chan, context, ies.called_number, 1)) {
- ast_log(LOG_WARNING,
- "Async goto of '%s' to '%s@%s' failed\n",
- ast_channel_name(bridged_chan), ies.called_number, context);
- } else {
- ast_debug(1, "Async goto of '%s' to '%s@%s' started\n",
- ast_channel_name(bridged_chan), ies.called_number, context);
- }
+ if (ast_bridge_transfer_blind(owner, ies.called_number,
+ context, NULL, NULL) != AST_BRIDGE_TRANSFER_SUCCESS) {
+ ast_log(LOG_WARNING, "Blind transfer of '%s' to '%s@%s' failed\n",
+ ast_channel_name(owner), ies.called_number,
+ context);
}
- ast_channel_unref(owner);
- ast_channel_unref(bridged_chan);
+ ast_channel_unref(owner);
ast_mutex_lock(&iaxsl[fr->callno]);
- } else {
- ast_debug(1, "Async goto not applicable on call %d\n", fr->callno);
- if (owner) {
- ast_channel_unlock(owner);
- }
}
break;
diff --git a/channels/chan_jingle.c b/channels/chan_jingle.c
index 717afae67..42dccf666 100644
--- a/channels/chan_jingle.c
+++ b/channels/chan_jingle.c
@@ -205,7 +205,6 @@ static struct ast_channel_tech jingle_tech = {
.send_text = jingle_sendtext,
.send_digit_begin = jingle_digit_begin,
.send_digit_end = jingle_digit_end,
- .bridge = ast_rtp_instance_bridge,
.call = jingle_call,
.hangup = jingle_hangup,
.answer = jingle_answer,
diff --git a/channels/chan_local.c b/channels/chan_local.c
deleted file mode 100644
index be4586fc8..000000000
--- a/channels/chan_local.c
+++ /dev/null
@@ -1,1452 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \brief Local Proxy Channel
- *
- * \ingroup channel_drivers
- */
-
-/*** MODULEINFO
- <support_level>core</support_level>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <fcntl.h>
-#include <sys/signal.h>
-
-#include "asterisk/lock.h"
-#include "asterisk/causes.h"
-#include "asterisk/channel.h"
-#include "asterisk/config.h"
-#include "asterisk/module.h"
-#include "asterisk/pbx.h"
-#include "asterisk/sched.h"
-#include "asterisk/io.h"
-#include "asterisk/acl.h"
-#include "asterisk/callerid.h"
-#include "asterisk/file.h"
-#include "asterisk/cli.h"
-#include "asterisk/app.h"
-#include "asterisk/musiconhold.h"
-#include "asterisk/manager.h"
-#include "asterisk/stringfields.h"
-#include "asterisk/devicestate.h"
-#include "asterisk/astobj2.h"
-
-/*** DOCUMENTATION
- <manager name="LocalOptimizeAway" language="en_US">
- <synopsis>
- Optimize away a local channel when possible.
- </synopsis>
- <syntax>
- <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
- <parameter name="Channel" required="true">
- <para>The channel name to optimize away.</para>
- </parameter>
- </syntax>
- <description>
- <para>A local channel created with "/n" will not automatically optimize away.
- Calling this command on the local channel will clear that flag and allow
- it to optimize away if it's bridged or when it becomes bridged.</para>
- </description>
- </manager>
- ***/
-
-static const char tdesc[] = "Local Proxy Channel Driver";
-
-#define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0)
-
-static struct ao2_container *locals;
-
-static unsigned int name_sequence = 0;
-
-static struct ast_jb_conf g_jb_conf = {
- .flags = 0,
- .max_size = -1,
- .resync_threshold = -1,
- .impl = "",
- .target_extra = -1,
-};
-
-static struct ast_channel *local_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause);
-static int local_digit_begin(struct ast_channel *ast, char digit);
-static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
-static int local_call(struct ast_channel *ast, const char *dest, int timeout);
-static int local_hangup(struct ast_channel *ast);
-static int local_answer(struct ast_channel *ast);
-static struct ast_frame *local_read(struct ast_channel *ast);
-static int local_write(struct ast_channel *ast, struct ast_frame *f);
-static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
-static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
-static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
-static int local_sendtext(struct ast_channel *ast, const char *text);
-static int local_devicestate(const char *data);
-static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
-static int local_queryoption(struct ast_channel *ast, int option, void *data, int *datalen);
-static int local_setoption(struct ast_channel *chan, int option, void *data, int datalen);
-
-/* PBX interface structure for channel registration */
-static struct ast_channel_tech local_tech = {
- .type = "Local",
- .description = tdesc,
- .requester = local_request,
- .send_digit_begin = local_digit_begin,
- .send_digit_end = local_digit_end,
- .call = local_call,
- .hangup = local_hangup,
- .answer = local_answer,
- .read = local_read,
- .write = local_write,
- .write_video = local_write,
- .exception = local_read,
- .indicate = local_indicate,
- .fixup = local_fixup,
- .send_html = local_sendhtml,
- .send_text = local_sendtext,
- .devicestate = local_devicestate,
- .bridged_channel = local_bridgedchannel,
- .queryoption = local_queryoption,
- .setoption = local_setoption,
-};
-
-/*!
- * \brief the local pvt structure for all channels
- *
- * The local channel pvt has two ast_chan objects - the "owner" and the "next channel", the outbound channel
- *
- * ast_chan owner -> local_pvt -> ast_chan chan -> yet-another-pvt-depending-on-channel-type
- */
-struct local_pvt {
- struct ast_channel *owner; /*!< Master Channel - Bridging happens here */
- struct ast_channel *chan; /*!< Outbound channel - PBX is run here */
- struct ast_format_cap *reqcap; /*!< Requested format capabilities */
- struct ast_jb_conf jb_conf; /*!< jitterbuffer configuration for this local channel */
- unsigned int flags; /*!< Private flags */
- char context[AST_MAX_CONTEXT]; /*!< Context to call */
- char exten[AST_MAX_EXTENSION]; /*!< Extension to call */
-};
-
-#define LOCAL_ALREADY_MASQED (1 << 0) /*!< Already masqueraded */
-#define LOCAL_LAUNCHED_PBX (1 << 1) /*!< PBX was launched */
-#define LOCAL_NO_OPTIMIZATION (1 << 2) /*!< Do not optimize using masquerading */
-#define LOCAL_BRIDGE (1 << 3) /*!< Report back the "true" channel as being bridged to */
-#define LOCAL_MOH_PASSTHRU (1 << 4) /*!< Pass through music on hold start/stop frames */
-
-/*!
- * \brief Send a pvt in with no locks held and get all locks
- *
- * \note NO locks should be held prior to calling this function
- * \note The pvt must have a ref held before calling this function
- * \note if outchan or outowner is set != NULL after calling this function
- * those channels are locked and reffed.
- * \note Batman.
- */
-static void awesome_locking(struct local_pvt *p, struct ast_channel **outchan, struct ast_channel **outowner)
-{
- struct ast_channel *chan = NULL;
- struct ast_channel *owner = NULL;
-
- ao2_lock(p);
- for (;;) {
- if (p->chan) {
- chan = p->chan;
- ast_channel_ref(chan);
- }
- if (p->owner) {
- owner = p->owner;
- ast_channel_ref(owner);
- }
- ao2_unlock(p);
-
- /* if we don't have both channels, then this is very easy */
- if (!owner || !chan) {
- if (owner) {
- ast_channel_lock(owner);
- } else if(chan) {
- ast_channel_lock(chan);
- }
- } else {
- /* lock both channels first, then get the pvt lock */
- ast_channel_lock_both(chan, owner);
- }
- ao2_lock(p);
-
- /* Now that we have all the locks, validate that nothing changed */
- if (p->owner != owner || p->chan != chan) {
- if (owner) {
- ast_channel_unlock(owner);
- owner = ast_channel_unref(owner);
- }
- if (chan) {
- ast_channel_unlock(chan);
- chan = ast_channel_unref(chan);
- }
- continue;
- }
-
- break;
- }
- *outowner = p->owner;
- *outchan = p->chan;
-}
-
-/* Called with ast locked */
-static int local_setoption(struct ast_channel *ast, int option, void *data, int datalen)
-{
- int res = 0;
- struct local_pvt *p;
- struct ast_channel *otherchan = NULL;
- ast_chan_write_info_t *write_info;
-
- if (option != AST_OPTION_CHANNEL_WRITE) {
- return -1;
- }
-
- write_info = data;
-
- if (write_info->version != AST_CHAN_WRITE_INFO_T_VERSION) {
- ast_log(LOG_ERROR, "The chan_write_info_t type has changed, and this channel hasn't been updated!\n");
- return -1;
- }
-
- if (!strcmp(write_info->function, "CHANNEL")
- && !strncasecmp(write_info->data, "hangup_handler_", 15)) {
- /* Block CHANNEL(hangup_handler_xxx) writes to the other local channel. */
- return 0;
- }
-
- /* get the tech pvt */
- if (!(p = ast_channel_tech_pvt(ast))) {
- return -1;
- }
- ao2_ref(p, 1);
- ast_channel_unlock(ast); /* Held when called, unlock before locking another channel */
-
- /* get the channel we are supposed to write to */
- ao2_lock(p);
- otherchan = (write_info->chan == p->owner) ? p->chan : p->owner;
- if (!otherchan || otherchan == write_info->chan) {
- res = -1;
- otherchan = NULL;
- ao2_unlock(p);
- goto setoption_cleanup;
- }
- ast_channel_ref(otherchan);
-
- /* clear the pvt lock before grabbing the channel */
- ao2_unlock(p);
-
- ast_channel_lock(otherchan);
- res = write_info->write_fn(otherchan, write_info->function, write_info->data, write_info->value);
- ast_channel_unlock(otherchan);
-
-setoption_cleanup:
- ao2_ref(p, -1);
- if (otherchan) {
- ast_channel_unref(otherchan);
- }
- ast_channel_lock(ast); /* Lock back before we leave */
- return res;
-}
-
-/*! \brief Adds devicestate to local channels */
-static int local_devicestate(const char *data)
-{
- char *exten = ast_strdupa(data);
- char *context;
- char *opts;
- int res;
- struct local_pvt *lp;
- struct ao2_iterator it;
-
- /* Strip options if they exist */
- opts = strchr(exten, '/');
- if (opts) {
- *opts = '\0';
- }
-
- context = strchr(exten, '@');
- if (!context) {
- ast_log(LOG_WARNING,
- "Someone used Local/%s somewhere without a @context. This is bad.\n", data);
- return AST_DEVICE_INVALID;
- }
- *context++ = '\0';
-
- ast_debug(3, "Checking if extension %s@%s exists (devicestate)\n", exten, context);
- res = ast_exists_extension(NULL, context, exten, 1, NULL);
- if (!res) {
- return AST_DEVICE_INVALID;
- }
-
- res = AST_DEVICE_NOT_INUSE;
-
- it = ao2_iterator_init(locals, 0);
- for (; (lp = ao2_iterator_next(&it)); ao2_ref(lp, -1)) {
- int is_inuse;
-
- ao2_lock(lp);
- is_inuse = !strcmp(exten, lp->exten)
- && !strcmp(context, lp->context)
- && lp->owner
- && ast_test_flag(lp, LOCAL_LAUNCHED_PBX);
- ao2_unlock(lp);
- if (is_inuse) {
- res = AST_DEVICE_INUSE;
- ao2_ref(lp, -1);
- break;
- }
- }
- ao2_iterator_destroy(&it);
-
- return res;
-}
-
-/*! \brief Return the bridged channel of a Local channel */
-static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
-{
- struct local_pvt *p = ast_channel_tech_pvt(bridge);
- struct ast_channel *bridged = bridge;
-
- if (!p) {
- ast_debug(1, "Asked for bridged channel on '%s'/'%s', returning <none>\n",
- ast_channel_name(chan), ast_channel_name(bridge));
- return NULL;
- }
-
- ao2_lock(p);
-
- if (ast_test_flag(p, LOCAL_BRIDGE)) {
- /* Find the opposite channel */
- bridged = (bridge == p->owner ? p->chan : p->owner);
-
- /* Now see if the opposite channel is bridged to anything */
- if (!bridged) {
- bridged = bridge;
- } else if (ast_channel_internal_bridged_channel(bridged)) {
- bridged = ast_channel_internal_bridged_channel(bridged);
- }
- }
-
- ao2_unlock(p);
-
- return bridged;
-}
-
-/* Called with ast locked */
-static int local_queryoption(struct ast_channel *ast, int option, void *data, int *datalen)
-{
- struct local_pvt *p;
- struct ast_channel *bridged = NULL;
- struct ast_channel *tmp = NULL;
- int res = 0;
-
- if (option != AST_OPTION_T38_STATE) {
- /* AST_OPTION_T38_STATE is the only supported option at this time */
- return -1;
- }
-
- /* for some reason the channel is not locked in channel.c when this function is called */
- if (!(p = ast_channel_tech_pvt(ast))) {
- return -1;
- }
-
- ao2_lock(p);
- if (!(tmp = IS_OUTBOUND(ast, p) ? p->owner : p->chan)) {
- ao2_unlock(p);
- return -1;
- }
- ast_channel_ref(tmp);
- ao2_unlock(p);
- ast_channel_unlock(ast); /* Held when called, unlock before locking another channel */
-
- ast_channel_lock(tmp);
- if (!(bridged = ast_bridged_channel(tmp))) {
- res = -1;
- ast_channel_unlock(tmp);
- goto query_cleanup;
- }
- ast_channel_ref(bridged);
- ast_channel_unlock(tmp);
-
-query_cleanup:
- if (bridged) {
- res = ast_channel_queryoption(bridged, option, data, datalen, 0);
- bridged = ast_channel_unref(bridged);
- }
- if (tmp) {
- tmp = ast_channel_unref(tmp);
- }
- ast_channel_lock(ast); /* Lock back before we leave */
-
- return res;
-}
-
-/*!
- * \brief queue a frame onto either the p->owner or p->chan
- *
- * \note the local_pvt MUST have it's ref count bumped before entering this function and
- * decremented after this function is called. This is a side effect of the deadlock
- * avoidance that is necessary to lock 2 channels and a tech_pvt. Without a ref counted
- * local_pvt, it is impossible to guarantee it will not be destroyed by another thread
- * during deadlock avoidance.
- */
-static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f,
- struct ast_channel *us, int us_locked)
-{
- struct ast_channel *other = NULL;
-
- /* Recalculate outbound channel */
- other = isoutbound ? p->owner : p->chan;
- if (!other) {
- return 0;
- }
-
- /* do not queue frame if generator is on both local channels */
- if (us && ast_channel_generator(us) && ast_channel_generator(other)) {
- return 0;
- }
-
- /* grab a ref on the channel before unlocking the pvt,
- * other can not go away from us now regardless of locking */
- ast_channel_ref(other);
- if (us && us_locked) {
- ast_channel_unlock(us);
- }
- ao2_unlock(p);
-
- if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_RINGING) {
- ast_setstate(other, AST_STATE_RINGING);
- }
- ast_queue_frame(other, f);
-
- other = ast_channel_unref(other);
- if (us && us_locked) {
- ast_channel_lock(us);
- }
- ao2_lock(p);
-
- return 0;
-}
-
-static int local_answer(struct ast_channel *ast)
-{
- struct local_pvt *p = ast_channel_tech_pvt(ast);
- int isoutbound;
- int res = -1;
-
- if (!p) {
- return -1;
- }
-
- ao2_ref(p, 1);
- ao2_lock(p);
- isoutbound = IS_OUTBOUND(ast, p);
- if (isoutbound) {
- /* Pass along answer since somebody answered us */
- struct ast_frame answer = { AST_FRAME_CONTROL, { AST_CONTROL_ANSWER } };
-
- res = local_queue_frame(p, isoutbound, &answer, ast, 1);
- } else {
- ast_log(LOG_WARNING, "Huh? Local is being asked to answer?\n");
- }
- ao2_unlock(p);
- ao2_ref(p, -1);
- return res;
-}
-
-/*!
- * \internal
- * \note This function assumes that we're only called from the "outbound" local channel side
- *
- * \note it is assummed p is locked and reffed before entering this function
- */
-static void check_bridge(struct ast_channel *ast, struct local_pvt *p)
-{
- struct ast_channel *owner;
- struct ast_channel *chan;
- struct ast_channel *bridged_chan;
- struct ast_frame *f;
-
- /* Do a few conditional checks early on just to see if this optimization is possible */
- if (ast_test_flag(p, LOCAL_NO_OPTIMIZATION | LOCAL_ALREADY_MASQED)
- || !p->chan || !p->owner) {
- return;
- }
-
- /* Safely get the channel bridged to p->chan */
- chan = ast_channel_ref(p->chan);
-
- ao2_unlock(p); /* don't call bridged channel with the pvt locked */
- bridged_chan = ast_bridged_channel(chan);
- ao2_lock(p);
-
- chan = ast_channel_unref(chan);
-
- /* since we had to unlock p to get the bridged chan, validate our
- * data once again and verify the bridged channel is what we expect
- * it to be in order to perform this optimization */
- if (ast_test_flag(p, LOCAL_NO_OPTIMIZATION | LOCAL_ALREADY_MASQED)
- || !p->chan || !p->owner
- || (ast_channel_internal_bridged_channel(p->chan) != bridged_chan)) {
- return;
- }
-
- /* only do the masquerade if we are being called on the outbound channel,
- if it has been bridged to another channel and if there are no pending
- frames on the owner channel (because they would be transferred to the
- outbound channel during the masquerade)
- */
- if (!ast_channel_internal_bridged_channel(p->chan) /* Not ast_bridged_channel! Only go one step! */
- || !AST_LIST_EMPTY(ast_channel_readq(p->owner))
- || ast != p->chan /* Sanity check (should always be false) */) {
- return;
- }
-
- /* Masquerade bridged channel into owner */
- /* Lock everything we need, one by one, and give up if
- we can't get everything. Remember, we'll get another
- chance in just a little bit */
- if (ast_channel_trylock(ast_channel_internal_bridged_channel(p->chan))) {
- return;
- }
- if (ast_check_hangup(ast_channel_internal_bridged_channel(p->chan))
- || ast_channel_trylock(p->owner)) {
- ast_channel_unlock(ast_channel_internal_bridged_channel(p->chan));
- return;
- }
-
- /*
- * At this point we have 4 locks:
- * p, p->chan (same as ast), p->chan->_bridge, p->owner
- *
- * Flush a voice or video frame on the outbound channel to make
- * the queue empty faster so we can get optimized out.
- */
- f = AST_LIST_FIRST(ast_channel_readq(p->chan));
- if (f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO)) {
- AST_LIST_REMOVE_HEAD(ast_channel_readq(p->chan), frame_list);
- ast_frfree(f);
- f = AST_LIST_FIRST(ast_channel_readq(p->chan));
- }
-
- if (f
- || ast_check_hangup(p->owner)
- || ast_channel_masquerade(p->owner, ast_channel_internal_bridged_channel(p->chan))) {
- ast_channel_unlock(p->owner);
- ast_channel_unlock(ast_channel_internal_bridged_channel(p->chan));
- return;
- }
-
- /* Masquerade got setup. */
- ast_debug(4, "Masquerading %s <- %s\n",
- ast_channel_name(p->owner),
- ast_channel_name(ast_channel_internal_bridged_channel(p->chan)));
- if (ast_channel_monitor(p->owner)
- && !ast_channel_monitor(ast_channel_internal_bridged_channel(p->chan))) {
- struct ast_channel_monitor *tmp;
-
- /* If a local channel is being monitored, we don't want a masquerade
- * to cause the monitor to go away. Since the masquerade swaps the monitors,
- * pre-swapping the monitors before the masquerade will ensure that the monitor
- * ends up where it is expected.
- */
- tmp = ast_channel_monitor(p->owner);
- ast_channel_monitor_set(p->owner, ast_channel_monitor(ast_channel_internal_bridged_channel(p->chan)));
- ast_channel_monitor_set(ast_channel_internal_bridged_channel(p->chan), tmp);
- }
- if (ast_channel_audiohooks(p->chan)) {
- struct ast_audiohook_list *audiohooks_swapper;
-
- audiohooks_swapper = ast_channel_audiohooks(p->chan);
- ast_channel_audiohooks_set(p->chan, ast_channel_audiohooks(p->owner));
- ast_channel_audiohooks_set(p->owner, audiohooks_swapper);
- }
-
- /* If any Caller ID was set, preserve it after masquerade like above. We must check
- * to see if Caller ID was set because otherwise we'll mistakingly copy info not
- * set from the dialplan and will overwrite the real channel Caller ID. The reason
- * for this whole preswapping action is because the Caller ID is set on the channel
- * thread (which is the to be masqueraded away local channel) before both local
- * channels are optimized away.
- */
- if (ast_channel_caller(p->owner)->id.name.valid || ast_channel_caller(p->owner)->id.number.valid
- || ast_channel_caller(p->owner)->id.subaddress.valid || ast_channel_caller(p->owner)->ani.name.valid
- || ast_channel_caller(p->owner)->ani.number.valid || ast_channel_caller(p->owner)->ani.subaddress.valid) {
- SWAP(*ast_channel_caller(p->owner), *ast_channel_caller(ast_channel_internal_bridged_channel(p->chan)));
- }
- if (ast_channel_redirecting(p->owner)->from.name.valid || ast_channel_redirecting(p->owner)->from.number.valid
- || ast_channel_redirecting(p->owner)->from.subaddress.valid || ast_channel_redirecting(p->owner)->to.name.valid
- || ast_channel_redirecting(p->owner)->to.number.valid || ast_channel_redirecting(p->owner)->to.subaddress.valid) {
- SWAP(*ast_channel_redirecting(p->owner), *ast_channel_redirecting(ast_channel_internal_bridged_channel(p->chan)));
- }
- if (ast_channel_dialed(p->owner)->number.str || ast_channel_dialed(p->owner)->subaddress.valid) {
- SWAP(*ast_channel_dialed(p->owner), *ast_channel_dialed(ast_channel_internal_bridged_channel(p->chan)));
- }
- ast_app_group_update(p->chan, p->owner);
- ast_set_flag(p, LOCAL_ALREADY_MASQED);
-
- ast_channel_unlock(p->owner);
- ast_channel_unlock(ast_channel_internal_bridged_channel(p->chan));
-
- /* Do the masquerade now. */
- owner = ast_channel_ref(p->owner);
- ao2_unlock(p);
- ast_channel_unlock(ast);
- ast_do_masquerade(owner);
- ast_channel_unref(owner);
- ast_channel_lock(ast);
- ao2_lock(p);
-}
-
-static struct ast_frame *local_read(struct ast_channel *ast)
-{
- return &ast_null_frame;
-}
-
-static int local_write(struct ast_channel *ast, struct ast_frame *f)
-{
- struct local_pvt *p = ast_channel_tech_pvt(ast);
- int res = -1;
- int isoutbound;
-
- if (!p) {
- return -1;
- }
-
- /* Just queue for delivery to the other side */
- ao2_ref(p, 1); /* ref for local_queue_frame */
- ao2_lock(p);
- isoutbound = IS_OUTBOUND(ast, p);
-
- if (isoutbound
- && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO)) {
- check_bridge(ast, p);
- }
-
- if (!ast_test_flag(p, LOCAL_ALREADY_MASQED)) {
- res = local_queue_frame(p, isoutbound, f, ast, 1);
- } else {
- ast_debug(1, "Not posting to '%s' queue since already masqueraded out\n",
- ast_channel_name(ast));
- res = 0;
- }
- ao2_unlock(p);
- ao2_ref(p, -1);
-
- return res;
-}
-
-static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
-{
- struct local_pvt *p = ast_channel_tech_pvt(newchan);
-
- if (!p) {
- return -1;
- }
-
- ao2_lock(p);
-
- if ((p->owner != oldchan) && (p->chan != oldchan)) {
- ast_log(LOG_WARNING, "Old channel %p wasn't %p or %p\n", oldchan, p->owner, p->chan);
- ao2_unlock(p);
- return -1;
- }
- if (p->owner == oldchan) {
- p->owner = newchan;
- } else {
- p->chan = newchan;
- }
-
- /* Do not let a masquerade cause a Local channel to be bridged to itself! */
- if (!ast_check_hangup(newchan)
- && ((p->owner && ast_channel_internal_bridged_channel(p->owner) == p->chan)
- || (p->chan && ast_channel_internal_bridged_channel(p->chan) == p->owner))) {
- ast_log(LOG_WARNING, "You can not bridge a Local channel to itself!\n");
- ao2_unlock(p);
- ast_queue_hangup(newchan);
- return -1;
- }
-
- ao2_unlock(p);
- return 0;
-}
-
-static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
-{
- struct local_pvt *p = ast_channel_tech_pvt(ast);
- int res = 0;
- struct ast_frame f = { AST_FRAME_CONTROL, };
- int isoutbound;
-
- if (!p) {
- return -1;
- }
-
- ao2_ref(p, 1); /* ref for local_queue_frame */
-
- /* If this is an MOH hold or unhold, do it on the Local channel versus real channel */
- if (!ast_test_flag(p, LOCAL_MOH_PASSTHRU) && condition == AST_CONTROL_HOLD) {
- ast_moh_start(ast, data, NULL);
- } else if (!ast_test_flag(p, LOCAL_MOH_PASSTHRU) && condition == AST_CONTROL_UNHOLD) {
- ast_moh_stop(ast);
- } else if (condition == AST_CONTROL_CONNECTED_LINE || condition == AST_CONTROL_REDIRECTING) {
- struct ast_channel *this_channel;
- struct ast_channel *the_other_channel;
-
- /* A connected line update frame may only contain a partial amount of data, such
- * as just a source, or just a ton, and not the full amount of information. However,
- * the collected information is all stored in the outgoing channel's connectedline
- * structure, so when receiving a connected line update on an outgoing local channel,
- * we need to transmit the collected connected line information instead of whatever
- * happens to be in this control frame. The same applies for redirecting information, which
- * is why it is handled here as well.*/
- ao2_lock(p);
- isoutbound = IS_OUTBOUND(ast, p);
- if (isoutbound) {
- this_channel = p->chan;
- the_other_channel = p->owner;
- } else {
- this_channel = p->owner;
- the_other_channel = p->chan;
- }
- if (the_other_channel) {
- unsigned char frame_data[1024];
-
- if (condition == AST_CONTROL_CONNECTED_LINE) {
- if (isoutbound) {
- ast_connected_line_copy_to_caller(ast_channel_caller(the_other_channel), ast_channel_connected(this_channel));
- }
- f.datalen = ast_connected_line_build_data(frame_data, sizeof(frame_data), ast_channel_connected(this_channel), NULL);
- } else {
- f.datalen = ast_redirecting_build_data(frame_data, sizeof(frame_data), ast_channel_redirecting(this_channel), NULL);
- }
- f.subclass.integer = condition;
- f.data.ptr = frame_data;
- res = local_queue_frame(p, isoutbound, &f, ast, 1);
- }
- ao2_unlock(p);
- } else {
- /* Queue up a frame representing the indication as a control frame */
- ao2_lock(p);
- /*
- * Block -1 stop tones events if we are to be optimized out. We
- * don't need a flurry of these events on a local channel chain
- * when initially connected to slow the optimization process.
- */
- if (0 <= condition || ast_test_flag(p, LOCAL_NO_OPTIMIZATION)) {
- isoutbound = IS_OUTBOUND(ast, p);
- f.subclass.integer = condition;
- f.data.ptr = (void *) data;
- f.datalen = datalen;
- res = local_queue_frame(p, isoutbound, &f, ast, 1);
-
- if (!res && condition == AST_CONTROL_T38_PARAMETERS
- && datalen == sizeof(struct ast_control_t38_parameters)) {
- const struct ast_control_t38_parameters *parameters = data;
-
- if (parameters->request_response == AST_T38_REQUEST_PARMS) {
- res = AST_T38_REQUEST_PARMS;
- }
- }
- } else {
- ast_debug(4, "Blocked indication %d\n", condition);
- }
- ao2_unlock(p);
- }
-
- ao2_ref(p, -1);
- return res;
-}
-
-static int local_digit_begin(struct ast_channel *ast, char digit)
-{
- struct local_pvt *p = ast_channel_tech_pvt(ast);
- int res = -1;
- struct ast_frame f = { AST_FRAME_DTMF_BEGIN, };
- int isoutbound;
-
- if (!p) {
- return -1;
- }
-
- ao2_ref(p, 1); /* ref for local_queue_frame */
- ao2_lock(p);
- isoutbound = IS_OUTBOUND(ast, p);
- f.subclass.integer = digit;
- res = local_queue_frame(p, isoutbound, &f, ast, 0);
- ao2_unlock(p);
- ao2_ref(p, -1);
-
- return res;
-}
-
-static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
-{
- struct local_pvt *p = ast_channel_tech_pvt(ast);
- int res = -1;
- struct ast_frame f = { AST_FRAME_DTMF_END, };
- int isoutbound;
-
- if (!p) {
- return -1;
- }
-
- ao2_ref(p, 1); /* ref for local_queue_frame */
- ao2_lock(p);
- isoutbound = IS_OUTBOUND(ast, p);
- f.subclass.integer = digit;
- f.len = duration;
- res = local_queue_frame(p, isoutbound, &f, ast, 0);
- ao2_unlock(p);
- ao2_ref(p, -1);
-
- return res;
-}
-
-static int local_sendtext(struct ast_channel *ast, const char *text)
-{
- struct local_pvt *p = ast_channel_tech_pvt(ast);
- int res = -1;
- struct ast_frame f = { AST_FRAME_TEXT, };
- int isoutbound;
-
- if (!p) {
- return -1;
- }
-
- ao2_ref(p, 1); /* ref for local_queue_frame */
- ao2_lock(p);
- isoutbound = IS_OUTBOUND(ast, p);
- f.data.ptr = (char *) text;
- f.datalen = strlen(text) + 1;
- res = local_queue_frame(p, isoutbound, &f, ast, 0);
- ao2_unlock(p);
- ao2_ref(p, -1);
- return res;
-}
-
-static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
-{
- struct local_pvt *p = ast_channel_tech_pvt(ast);
- int res = -1;
- struct ast_frame f = { AST_FRAME_HTML, };
- int isoutbound;
-
- if (!p) {
- return -1;
- }
-
- ao2_ref(p, 1); /* ref for local_queue_frame */
- ao2_lock(p);
- isoutbound = IS_OUTBOUND(ast, p);
- f.subclass.integer = subclass;
- f.data.ptr = (char *)data;
- f.datalen = datalen;
- res = local_queue_frame(p, isoutbound, &f, ast, 0);
- ao2_unlock(p);
- ao2_ref(p, -1);
-
- return res;
-}
-
-/*! \brief Initiate new call, part of PBX interface
- * dest is the dial string */
-static int local_call(struct ast_channel *ast, const char *dest, int timeout)
-{
- struct local_pvt *p = ast_channel_tech_pvt(ast);
- int pvt_locked = 0;
-
- struct ast_channel *owner = NULL;
- struct ast_channel *chan = NULL;
- int res;
- struct ast_var_t *varptr;
- struct ast_var_t *clone_var;
- char *reduced_dest = ast_strdupa(dest);
- char *slash;
- const char *exten;
- const char *context;
-
- if (!p) {
- return -1;
- }
-
- /* since we are letting go of channel locks that were locked coming into
- * this function, then we need to give the tech pvt a ref */
- ao2_ref(p, 1);
- ast_channel_unlock(ast);
-
- awesome_locking(p, &chan, &owner);
- pvt_locked = 1;
-
- if (owner != ast) {
- res = -1;
- goto return_cleanup;
- }
-
- if (!owner || !chan) {
- res = -1;
- goto return_cleanup;
- }
-
- /*
- * Note that cid_num and cid_name aren't passed in the ast_channel_alloc
- * call, so it's done here instead.
- *
- * All these failure points just return -1. The individual strings will
- * be cleared when we destroy the channel.
- */
- ast_party_redirecting_copy(ast_channel_redirecting(chan), ast_channel_redirecting(owner));
-
- ast_party_dialed_copy(ast_channel_dialed(chan), ast_channel_dialed(owner));
-
- ast_connected_line_copy_to_caller(ast_channel_caller(chan), ast_channel_connected(owner));
- ast_connected_line_copy_from_caller(ast_channel_connected(chan), ast_channel_caller(owner));
-
- ast_channel_language_set(chan, ast_channel_language(owner));
- ast_channel_accountcode_set(chan, ast_channel_accountcode(owner));
- ast_channel_musicclass_set(chan, ast_channel_musicclass(owner));
- ast_cdr_update(chan);
-
- ast_channel_cc_params_init(chan, ast_channel_get_cc_config_params(owner));
-
- /* Make sure we inherit the AST_CAUSE_ANSWERED_ELSEWHERE if it's set on the queue/dial call request in the dialplan */
- if (ast_channel_hangupcause(ast) == AST_CAUSE_ANSWERED_ELSEWHERE) {
- ast_channel_hangupcause_set(chan, AST_CAUSE_ANSWERED_ELSEWHERE);
- }
-
- /* copy the channel variables from the incoming channel to the outgoing channel */
- /* Note that due to certain assumptions, they MUST be in the same order */
- AST_LIST_TRAVERSE(ast_channel_varshead(owner), varptr, entries) {
- clone_var = ast_var_assign(varptr->name, varptr->value);
- if (clone_var) {
- AST_LIST_INSERT_TAIL(ast_channel_varshead(chan), clone_var, entries);
- }
- }
- ast_channel_datastore_inherit(owner, chan);
- /* If the local channel has /n or /b on the end of it,
- * we need to lop that off for our argument to setting
- * up the CC_INTERFACES variable
- */
- if ((slash = strrchr(reduced_dest, '/'))) {
- *slash = '\0';
- }
- ast_set_cc_interfaces_chanvar(chan, reduced_dest);
-
- exten = ast_strdupa(ast_channel_exten(chan));
- context = ast_strdupa(ast_channel_context(chan));
-
- ao2_unlock(p);
- pvt_locked = 0;
-
- ast_channel_unlock(chan);
-
- if (!ast_exists_extension(chan, context, exten, 1,
- S_COR(ast_channel_caller(owner)->id.number.valid, ast_channel_caller(owner)->id.number.str, NULL))) {
- ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", exten, context);
- res = -1;
- chan = ast_channel_unref(chan); /* we already unlocked it, so clear it here so the cleanup label won't touch it. */
- goto return_cleanup;
- }
-
- /*** DOCUMENTATION
- <managerEventInstance>
- <synopsis>Raised when two halves of a Local Channel form a bridge.</synopsis>
- <syntax>
- <parameter name="Channel1">
- <para>The name of the Local Channel half that bridges to another channel.</para>
- </parameter>
- <parameter name="Channel2">
- <para>The name of the Local Channel half that executes the dialplan.</para>
- </parameter>
- <parameter name="Context">
- <para>The context in the dialplan that Channel2 starts in.</para>
- </parameter>
- <parameter name="Exten">
- <para>The extension in the dialplan that Channel2 starts in.</para>
- </parameter>
- <parameter name="LocalOptimization">
- <enumlist>
- <enum name="Yes"/>
- <enum name="No"/>
- </enumlist>
- </parameter>
- </syntax>
- </managerEventInstance>
- ***/
- manager_event(EVENT_FLAG_CALL, "LocalBridge",
- "Channel1: %s\r\n"
- "Channel2: %s\r\n"
- "Uniqueid1: %s\r\n"
- "Uniqueid2: %s\r\n"
- "Context: %s\r\n"
- "Exten: %s\r\n"
- "LocalOptimization: %s\r\n",
- ast_channel_name(p->owner), ast_channel_name(p->chan),
- ast_channel_uniqueid(p->owner), ast_channel_uniqueid(p->chan),
- p->context, p->exten,
- ast_test_flag(p, LOCAL_NO_OPTIMIZATION) ? "Yes" : "No");
-
-
- /* Start switch on sub channel */
- res = ast_pbx_start(chan);
- if (!res) {
- ao2_lock(p);
- ast_set_flag(p, LOCAL_LAUNCHED_PBX);
- ao2_unlock(p);
- }
- chan = ast_channel_unref(chan); /* chan is already unlocked, clear it here so the cleanup lable won't touch it. */
-
-return_cleanup:
- if (p) {
- if (pvt_locked) {
- ao2_unlock(p);
- }
- ao2_ref(p, -1);
- }
- if (chan) {
- ast_channel_unlock(chan);
- chan = ast_channel_unref(chan);
- }
-
- /* owner is supposed to be == to ast, if it
- * is, don't unlock it because ast must exit locked */
- if (owner) {
- if (owner != ast) {
- ast_channel_unlock(owner);
- ast_channel_lock(ast);
- }
- owner = ast_channel_unref(owner);
- } else {
- /* we have to exit with ast locked */
- ast_channel_lock(ast);
- }
-
- return res;
-}
-
-/*! \brief Hangup a call through the local proxy channel */
-static int local_hangup(struct ast_channel *ast)
-{
- struct local_pvt *p = ast_channel_tech_pvt(ast);
- int isoutbound;
- int hangup_chan = 0;
- int res = 0;
- struct ast_frame f = { AST_FRAME_CONTROL, { AST_CONTROL_HANGUP }, .data.uint32 = ast_channel_hangupcause(ast) };
- struct ast_channel *owner = NULL;
- struct ast_channel *chan = NULL;
-
- if (!p) {
- return -1;
- }
-
- /* give the pvt a ref since we are unlocking the channel. */
- ao2_ref(p, 1);
-
- /* the pvt isn't going anywhere, we gave it a ref */
- ast_channel_unlock(ast);
-
- /* lock everything */
- awesome_locking(p, &chan, &owner);
-
- if (ast != chan && ast != owner) {
- res = -1;
- goto local_hangup_cleanup;
- }
-
- isoutbound = IS_OUTBOUND(ast, p); /* just comparing pointer of ast */
-
- if (p->chan && ast_channel_hangupcause(ast) == AST_CAUSE_ANSWERED_ELSEWHERE) {
- ast_channel_hangupcause_set(p->chan, AST_CAUSE_ANSWERED_ELSEWHERE);
- ast_debug(2, "This local call has AST_CAUSE_ANSWERED_ELSEWHERE set.\n");
- }
-
- if (isoutbound) {
- const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS");
-
- if (status && p->owner) {
- ast_channel_hangupcause_set(p->owner, ast_channel_hangupcause(p->chan));
- pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status);
- }
-
- ast_clear_flag(p, LOCAL_LAUNCHED_PBX);
- p->chan = NULL;
- } else {
- if (p->chan) {
- ast_queue_hangup(p->chan);
- }
- p->owner = NULL;
- }
-
- ast_channel_tech_pvt_set(ast, NULL); /* this is one of our locked channels, doesn't matter which */
-
- if (!p->owner && !p->chan) {
- ao2_unlock(p);
-
- ao2_unlink(locals, p);
- ao2_ref(p, -1);
- p = NULL;
- res = 0;
- goto local_hangup_cleanup;
- }
- if (p->chan && !ast_test_flag(p, LOCAL_LAUNCHED_PBX)) {
- /* Need to actually hangup since there is no PBX */
- hangup_chan = 1;
- } else {
- local_queue_frame(p, isoutbound, &f, NULL, 0);
- }
-
-local_hangup_cleanup:
- if (p) {
- ao2_unlock(p);
- ao2_ref(p, -1);
- }
- if (owner) {
- ast_channel_unlock(owner);
- owner = ast_channel_unref(owner);
- }
- if (chan) {
- ast_channel_unlock(chan);
- if (hangup_chan) {
- ast_hangup(chan);
- }
- chan = ast_channel_unref(chan);
- }
-
- /* leave with the same stupid channel locked that came in */
- ast_channel_lock(ast);
- return res;
-}
-
-/*!
- * \internal
- * \brief struct local_pvt destructor.
- *
- * \param vdoomed Void local_pvt to destroy.
- *
- * \return Nothing
- */
-static void local_pvt_destructor(void *vdoomed)
-{
- struct local_pvt *doomed = vdoomed;
-
- doomed->reqcap = ast_format_cap_destroy(doomed->reqcap);
-
- ast_module_unref(ast_module_info->self);
-}
-
-/*! \brief Create a call structure */
-static struct local_pvt *local_alloc(const char *data, struct ast_format_cap *cap)
-{
- struct local_pvt *tmp = NULL;
- char *parse;
- char *c = NULL;
- char *opts = NULL;
-
- if (!(tmp = ao2_alloc(sizeof(*tmp), local_pvt_destructor))) {
- return NULL;
- }
- if (!(tmp->reqcap = ast_format_cap_dup(cap))) {
- ao2_ref(tmp, -1);
- return NULL;
- }
-
- ast_module_ref(ast_module_info->self);
-
- /* Initialize private structure information */
- parse = ast_strdupa(data);
-
- memcpy(&tmp->jb_conf, &g_jb_conf, sizeof(tmp->jb_conf));
-
- /* Look for options */
- if ((opts = strchr(parse, '/'))) {
- *opts++ = '\0';
- if (strchr(opts, 'n'))
- ast_set_flag(tmp, LOCAL_NO_OPTIMIZATION);
- if (strchr(opts, 'j')) {
- if (ast_test_flag(tmp, LOCAL_NO_OPTIMIZATION))
- ast_set_flag(&tmp->jb_conf, AST_JB_ENABLED);
- else {
- ast_log(LOG_ERROR, "You must use the 'n' option with the 'j' option to enable the jitter buffer\n");
- }
- }
- if (strchr(opts, 'b')) {
- ast_set_flag(tmp, LOCAL_BRIDGE);
- }
- if (strchr(opts, 'm')) {
- ast_set_flag(tmp, LOCAL_MOH_PASSTHRU);
- }
- }
-
- /* Look for a context */
- if ((c = strchr(parse, '@'))) {
- *c++ = '\0';
- }
-
- ast_copy_string(tmp->context, c ? c : "default", sizeof(tmp->context));
- ast_copy_string(tmp->exten, parse, sizeof(tmp->exten));
-
- ao2_link(locals, tmp);
-
- return tmp; /* this is returned with a ref */
-}
-
-/*! \brief Start new local channel */
-static struct ast_channel *local_new(struct local_pvt *p, int state, const char *linkedid, struct ast_callid *callid)
-{
- struct ast_channel *owner;
- struct ast_channel *chan;
- struct ast_format fmt;
- int generated_seqno = ast_atomic_fetchadd_int((int *)&name_sequence, +1);
-
- /*
- * Allocate two new Asterisk channels
- *
- * Make sure that the ;2 channel gets the same linkedid as ;1.
- * You can't pass linkedid to both allocations since if linkedid
- * isn't set, then each channel will generate its own linkedid.
- */
- if (!(owner = ast_channel_alloc(1, state, NULL, NULL, NULL,
- p->exten, p->context, linkedid, 0,
- "Local/%s@%s-%08x;1", p->exten, p->context, generated_seqno))
- || !(chan = ast_channel_alloc(1, AST_STATE_RING, NULL, NULL, NULL,
- p->exten, p->context, ast_channel_linkedid(owner), 0,
- "Local/%s@%s-%08x;2", p->exten, p->context, generated_seqno))) {
- if (owner) {
- owner = ast_channel_release(owner);
- }
- ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n");
- return NULL;
- }
-
- if (callid) {
- ast_channel_callid_set(owner, callid);
- ast_channel_callid_set(chan, callid);
- }
-
- ast_channel_tech_set(owner, &local_tech);
- ast_channel_tech_set(chan, &local_tech);
- ast_channel_tech_pvt_set(owner, p);
- ast_channel_tech_pvt_set(chan, p);
-
- ast_format_cap_copy(ast_channel_nativeformats(owner), p->reqcap);
- ast_format_cap_copy(ast_channel_nativeformats(chan), p->reqcap);
-
- /* Determine our read/write format and set it on each channel */
- ast_best_codec(p->reqcap, &fmt);
- ast_format_copy(ast_channel_writeformat(owner), &fmt);
- ast_format_copy(ast_channel_writeformat(chan), &fmt);
- ast_format_copy(ast_channel_rawwriteformat(owner), &fmt);
- ast_format_copy(ast_channel_rawwriteformat(chan), &fmt);
- ast_format_copy(ast_channel_readformat(owner), &fmt);
- ast_format_copy(ast_channel_readformat(chan), &fmt);
- ast_format_copy(ast_channel_rawreadformat(owner), &fmt);
- ast_format_copy(ast_channel_rawreadformat(chan), &fmt);
-
- ast_set_flag(ast_channel_flags(owner), AST_FLAG_DISABLE_DEVSTATE_CACHE);
- ast_set_flag(ast_channel_flags(chan), AST_FLAG_DISABLE_DEVSTATE_CACHE);
-
- p->owner = owner;
- p->chan = chan;
-
- ast_jb_configure(owner, &p->jb_conf);
-
- return owner;
-}
-
-/*! \brief Part of PBX interface */
-static struct ast_channel *local_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause)
-{
- struct local_pvt *p;
- struct ast_channel *chan;
- struct ast_callid *callid = ast_read_threadstorage_callid();
-
- /* Allocate a new private structure and then Asterisk channel */
- p = local_alloc(data, cap);
- if (!p) {
- chan = NULL;
- goto local_request_end;
- }
- chan = local_new(p, AST_STATE_DOWN, requestor ? ast_channel_linkedid(requestor) : NULL, callid);
- if (!chan) {
- ao2_unlink(locals, p);
- } else if (ast_channel_cc_params_init(chan, requestor ? ast_channel_get_cc_config_params((struct ast_channel *)requestor) : NULL)) {
- ao2_unlink(locals, p);
- p->owner = ast_channel_release(p->owner);
- p->chan = ast_channel_release(p->chan);
- chan = NULL;
- }
- ao2_ref(p, -1); /* kill the ref from the alloc */
-
-local_request_end:
-
- if (callid) {
- ast_callid_unref(callid);
- }
-
- return chan;
-}
-
-/*! \brief CLI command "local show channels" */
-static char *locals_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- struct local_pvt *p = NULL;
- struct ao2_iterator it;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "local show channels";
- e->usage =
- "Usage: local show channels\n"
- " Provides summary information on active local proxy channels.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- if (a->argc != 3) {
- return CLI_SHOWUSAGE;
- }
-
- if (ao2_container_count(locals) == 0) {
- ast_cli(a->fd, "No local channels in use\n");
- return RESULT_SUCCESS;
- }
-
- it = ao2_iterator_init(locals, 0);
- while ((p = ao2_iterator_next(&it))) {
- ao2_lock(p);
- ast_cli(a->fd, "%s -- %s@%s\n", p->owner ? ast_channel_name(p->owner) : "<unowned>", p->exten, p->context);
- ao2_unlock(p);
- ao2_ref(p, -1);
- }
- ao2_iterator_destroy(&it);
-
- return CLI_SUCCESS;
-}
-
-static struct ast_cli_entry cli_local[] = {
- AST_CLI_DEFINE(locals_show, "List status of local channels"),
-};
-
-static int manager_optimize_away(struct mansession *s, const struct message *m)
-{
- const char *channel;
- struct local_pvt *p;
- struct local_pvt *found;
- struct ast_channel *chan;
-
- channel = astman_get_header(m, "Channel");
- if (ast_strlen_zero(channel)) {
- astman_send_error(s, m, "'Channel' not specified.");
- return 0;
- }
-
- chan = ast_channel_get_by_name(channel);
- if (!chan) {
- astman_send_error(s, m, "Channel does not exist.");
- return 0;
- }
-
- p = ast_channel_tech_pvt(chan);
- ast_channel_unref(chan);
-
- found = p ? ao2_find(locals, p, 0) : NULL;
- if (found) {
- ao2_lock(found);
- ast_clear_flag(found, LOCAL_NO_OPTIMIZATION);
- ao2_unlock(found);
- ao2_ref(found, -1);
- astman_send_ack(s, m, "Queued channel to be optimized away");
- } else {
- astman_send_error(s, m, "Unable to find channel");
- }
-
- return 0;
-}
-
-
-static int locals_cmp_cb(void *obj, void *arg, int flags)
-{
- return (obj == arg) ? CMP_MATCH : 0;
-}
-
-/*! \brief Load module into PBX, register channel */
-static int load_module(void)
-{
- if (!(local_tech.capabilities = ast_format_cap_alloc())) {
- return AST_MODULE_LOAD_FAILURE;
- }
- ast_format_cap_add_all(local_tech.capabilities);
-
- locals = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, locals_cmp_cb);
- if (!locals) {
- ast_format_cap_destroy(local_tech.capabilities);
- return AST_MODULE_LOAD_FAILURE;
- }
-
- /* Make sure we can register our channel type */
- if (ast_channel_register(&local_tech)) {
- ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n");
- ao2_ref(locals, -1);
- ast_format_cap_destroy(local_tech.capabilities);
- return AST_MODULE_LOAD_FAILURE;
- }
- ast_cli_register_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
- ast_manager_register_xml("LocalOptimizeAway", EVENT_FLAG_SYSTEM|EVENT_FLAG_CALL, manager_optimize_away);
-
- return AST_MODULE_LOAD_SUCCESS;
-}
-
-/*! \brief Unload the local proxy channel from Asterisk */
-static int unload_module(void)
-{
- struct local_pvt *p = NULL;
- struct ao2_iterator it;
-
- /* First, take us out of the channel loop */
- ast_cli_unregister_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
- ast_manager_unregister("LocalOptimizeAway");
- ast_channel_unregister(&local_tech);
-
- it = ao2_iterator_init(locals, 0);
- while ((p = ao2_iterator_next(&it))) {
- if (p->owner) {
- ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
- }
- ao2_ref(p, -1);
- }
- ao2_iterator_destroy(&it);
- ao2_ref(locals, -1);
-
- ast_format_cap_destroy(local_tech.capabilities);
- return 0;
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Local Proxy Channel (Note: used internally by other modules)",
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER,
- );
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index e254823bb..1f0830762 100644
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -83,6 +83,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/chanvars.h"
#include "asterisk/pktccops.h"
#include "asterisk/stasis.h"
+#include "asterisk/bridging.h"
/*
* Define to work around buggy dlink MGCP phone firmware which
@@ -480,7 +481,6 @@ static struct ast_channel_tech mgcp_tech = {
.fixup = mgcp_fixup,
.send_digit_begin = mgcp_senddigit_begin,
.send_digit_end = mgcp_senddigit_end,
- .bridge = ast_rtp_instance_bridge,
.func_channel_read = acf_channel_read,
};
@@ -3213,56 +3213,55 @@ static void *mgcp_ss(void *data)
return NULL;
}
-static int attempt_transfer(struct mgcp_endpoint *p)
+/*! \brief Complete an attended transfer
+ *
+ * \param p The endpoint performing the attended transfer
+ * \param sub The sub-channel completing the attended transfer
+ *
+ * \note p->sub is the currently active sub-channel (the channel the phone is using)
+ * \note p->sub->next is the sub-channel not in use, potentially on hold
+ *
+ * \retval 0 when channel should be hung up
+ * \retval 1 when channel should not be hung up
+ */
+static int attempt_transfer(struct mgcp_endpoint *p, struct mgcp_subchannel *sub)
{
- /* *************************
- * I hope this works.
- * Copied out of chan_zap
- * Cross your fingers
- * *************************/
-
- /* In order to transfer, we need at least one of the channels to
- actually be in a call bridge. We can't conference two applications
- together (but then, why would we want to?) */
- if (ast_bridged_channel(p->sub->owner)) {
- /* The three-way person we're about to transfer to could still be in MOH, so
- stop it now */
- ast_queue_control(p->sub->next->owner, AST_CONTROL_UNHOLD);
- if (ast_channel_state(p->sub->owner) == AST_STATE_RINGING) {
- ast_queue_control(p->sub->next->owner, AST_CONTROL_RINGING);
- }
- if (ast_channel_masquerade(p->sub->next->owner, ast_bridged_channel(p->sub->owner))) {
- ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
- ast_channel_name(ast_bridged_channel(p->sub->owner)), ast_channel_name(p->sub->next->owner));
- return -1;
- }
- /* Orphan the channel */
- unalloc_sub(p->sub->next);
- } else if (ast_bridged_channel(p->sub->next->owner)) {
- if (ast_channel_state(p->sub->owner) == AST_STATE_RINGING) {
- ast_queue_control(p->sub->next->owner, AST_CONTROL_RINGING);
- }
- ast_queue_control(p->sub->next->owner, AST_CONTROL_UNHOLD);
- if (ast_channel_masquerade(p->sub->owner, ast_bridged_channel(p->sub->next->owner))) {
- ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
- ast_channel_name(ast_bridged_channel(p->sub->next->owner)), ast_channel_name(p->sub->owner));
- return -1;
+ enum ast_transfer_result res;
+
+ /* Ensure that the other channel goes off hold and that it is indicating properly */
+ ast_queue_control(sub->next->owner, AST_CONTROL_UNHOLD);
+ if (ast_channel_state(sub->owner) == AST_STATE_RINGING) {
+ ast_queue_control(sub->next->owner, AST_CONTROL_RINGING);
+ }
+
+ ast_mutex_unlock(&p->sub->next->lock);
+ ast_mutex_unlock(&p->sub->lock);
+ res = ast_bridge_transfer_attended(sub->owner, sub->next->owner, NULL);
+
+ /* Subs are only freed when the endpoint itself is destroyed, so they will continue to exist
+ * after ast_bridge_transfer_attended returns making this safe without reference counting
+ */
+ ast_mutex_lock(&p->sub->lock);
+ ast_mutex_lock(&p->sub->next->lock);
+
+ if (res != AST_BRIDGE_TRANSFER_SUCCESS) {
+ /* If transferring fails hang up the other channel if present and us */
+ if (sub->next->owner) {
+ ast_channel_softhangup_internal_flag_add(sub->next->owner, AST_SOFTHANGUP_DEV);
+ mgcp_queue_hangup(sub->next);
}
- /*swap_subs(p, SUB_THREEWAY, SUB_REAL);*/
- ast_verb(3, "Swapping %d for %d on %s@%s\n", p->sub->id, p->sub->next->id, p->name, p->parent->name);
- p->sub = p->sub->next;
- unalloc_sub(p->sub->next);
- /* Tell the caller not to hangup */
+ sub->next->alreadygone = 1;
+ return 0;
+ }
+
+ unalloc_sub(sub->next);
+
+ /* If the active sub is NOT the one completing the transfer change it to be, and hang up the other sub */
+ if (p->sub != sub) {
+ p->sub = sub;
return 1;
- } else {
- ast_debug(1, "Neither %s nor %s are in a bridge, nothing to transfer\n",
- ast_channel_name(p->sub->owner), ast_channel_name(p->sub->next->owner));
- ast_channel_softhangup_internal_flag_add(p->sub->next->owner, AST_SOFTHANGUP_DEV);
- if (p->sub->next->owner) {
- p->sub->next->alreadygone = 1;
- mgcp_queue_hangup(p->sub->next);
- }
}
+
return 0;
}
@@ -3511,13 +3510,8 @@ static int handle_request(struct mgcp_subchannel *sub, struct mgcp_request *req,
/* We're allowed to transfer, we have two avtive calls and */
/* we made at least one of the calls. Let's try and transfer */
ast_mutex_lock(&p->sub->next->lock);
- res = attempt_transfer(p);
- if (res < 0) {
- if (p->sub->next->owner) {
- sub->next->alreadygone = 1;
- mgcp_queue_hangup(sub->next);
- }
- } else if (res) {
+ res = attempt_transfer(p, sub);
+ if (res) {
ast_log(LOG_WARNING, "Transfer attempt failed\n");
ast_mutex_unlock(&p->sub->next->lock);
return -1;
diff --git a/channels/chan_misdn.c b/channels/chan_misdn.c
index 2bc6f1e35..aaa7170b3 100644
--- a/channels/chan_misdn.c
+++ b/channels/chan_misdn.c
@@ -8091,6 +8091,7 @@ static int misdn_send_text(struct ast_channel *chan, const char *text)
return 0;
}
+/* BUGBUG The mISDN channel driver needs its own native bridge technology. (More like just never give it one.) */
static struct ast_channel_tech misdn_tech = {
.type = misdn_type,
.description = "Channel driver for mISDN Support (Bri/Pri)",
diff --git a/channels/chan_motif.c b/channels/chan_motif.c
index 0836972e1..56b06b145 100644
--- a/channels/chan_motif.c
+++ b/channels/chan_motif.c
@@ -360,7 +360,6 @@ static struct ast_channel_tech jingle_tech = {
.send_text = jingle_sendtext,
.send_digit_begin = jingle_digit_begin,
.send_digit_end = jingle_digit_end,
- .bridge = ast_rtp_instance_bridge,
.call = jingle_call,
.hangup = jingle_hangup,
.answer = jingle_answer,
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 88965fc73..f7a528b68 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -176,7 +176,6 @@
/*** MODULEINFO
<use type="module">res_crypto</use>
<use type="module">res_http_websocket</use>
- <depend>chan_local</depend>
<support_level>core</support_level>
***/
@@ -295,6 +294,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "sip/include/security_events.h"
#include "asterisk/sip_api.h"
#include "asterisk/app.h"
+#include "asterisk/bridging.h"
#include "asterisk/stasis_endpoints.h"
/*** DOCUMENTATION
@@ -1202,8 +1202,6 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
struct sip_request *req, const char *uri);
static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag);
static void check_pendings(struct sip_pvt *p);
-static void *sip_park_thread(void *stuff);
-static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, uint32_t seqno, const char *park_exten, const char *park_context);
static void *sip_pickup_thread(void *stuff);
static int sip_pickup(struct ast_channel *chan);
@@ -1544,7 +1542,6 @@ struct ast_channel_tech sip_tech = {
.fixup = sip_fixup, /* called with chan locked */
.send_digit_begin = sip_senddigit_begin, /* called with chan unlocked */
.send_digit_end = sip_senddigit_end,
- .bridge = ast_rtp_instance_bridge, /* XXX chan unlocked ? */
.early_bridge = ast_rtp_instance_early_bridge,
.send_text = sip_sendtext, /* called with chan locked */
.func_channel_read = sip_acf_channel_read,
@@ -24426,160 +24423,6 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
}
}
-
-/*! \brief Park SIP call support function
- Starts in a new thread, then parks the call
- XXX Should we add a wait period after streaming audio and before hangup?? Sometimes the
- audio can't be heard before hangup
-*/
-static void *sip_park_thread(void *stuff)
-{
- struct ast_channel *transferee, *transferer; /* Chan1: The transferee, Chan2: The transferer */
- struct sip_dual *d;
- int ext;
- int res;
-
- d = stuff;
- transferee = d->chan1;
- transferer = d->chan2;
-
- ast_debug(4, "SIP Park: Transferer channel %s, Transferee %s\n", ast_channel_name(transferer), ast_channel_name(transferee));
-
- res = ast_park_call_exten(transferee, transferer, d->park_exten, d->park_context, 0, &ext);
-
- sip_pvt_lock(ast_channel_tech_pvt(transferer));
-#ifdef WHEN_WE_KNOW_THAT_THE_CLIENT_SUPPORTS_MESSAGE
- if (res) {
- destroy_msg_headers(ast_channel_tech_pvt(transferer));
- ast_string_field_set(ast_channel_tech_pvt(transferer), msg_body, "Unable to park call.");
- transmit_message(ast_channel_tech_pvt(transferer), 0, 0);
- } else {
- /* Then tell the transferer what happened */
- destroy_msg_headers(ast_channel_tech_pvt(transferer));
- sprintf(buf, "Call parked on extension '%d'.", ext);
- ast_string_field_set(ast_channel_tech_pvt(transferer), msg_body, buf);
- transmit_message(ast_channel_tech_pvt(transferer), 0, 0);
- }
-#endif
-
- /* Any way back to the current call??? */
- /* Transmit response to the REFER request */
- if (!res) {
- /* Transfer succeeded */
- append_history(ast_channel_tech_pvt(transferer), "SIPpark", "Parked call on %d", ext);
- transmit_notify_with_sipfrag(ast_channel_tech_pvt(transferer), d->seqno, "200 OK", TRUE);
- sip_pvt_unlock(ast_channel_tech_pvt(transferer));
- ast_channel_hangupcause_set(transferer, AST_CAUSE_NORMAL_CLEARING);
- ast_hangup(transferer); /* This will cause a BYE */
- ast_debug(1, "SIP Call parked on extension '%d'\n", ext);
- } else {
- transmit_notify_with_sipfrag(ast_channel_tech_pvt(transferer), d->seqno, "503 Service Unavailable", TRUE);
- append_history(ast_channel_tech_pvt(transferer), "SIPpark", "Parking failed\n");
- sip_pvt_unlock(ast_channel_tech_pvt(transferer));
- ast_debug(1, "SIP Call parked failed \n");
- /* Do not hangup call */
- }
- deinit_req(&d->req);
- ast_free(d->park_exten);
- ast_free(d->park_context);
- ast_free(d);
- return NULL;
-}
-
-/*! DO NOT hold any locks while calling sip_park */
-static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, uint32_t seqno, const char *park_exten, const char *park_context)
-{
- struct sip_dual *d;
- struct ast_channel *transferee, *transferer;
- pthread_t th;
-
- transferee = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, ast_channel_accountcode(chan1), ast_channel_exten(chan1), ast_channel_context(chan1), ast_channel_linkedid(chan1), ast_channel_amaflags(chan1), "Parking/%s", ast_channel_name(chan1));
- transferer = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, ast_channel_accountcode(chan2), ast_channel_exten(chan2), ast_channel_context(chan2), ast_channel_linkedid(chan2), ast_channel_amaflags(chan2), "SIPPeer/%s", ast_channel_name(chan2));
- d = ast_calloc(1, sizeof(*d));
- if (!transferee || !transferer || !d) {
- if (transferee) {
- ast_hangup(transferee);
- }
- if (transferer) {
- ast_hangup(transferer);
- }
- ast_free(d);
- return -1;
- }
- d->park_exten = ast_strdup(park_exten);
- d->park_context = ast_strdup(park_context);
- if (!d->park_exten || !d->park_context) {
- ast_hangup(transferee);
- ast_hangup(transferer);
- ast_free(d->park_exten);
- ast_free(d->park_context);
- ast_free(d);
- return -1;
- }
-
- /* Make formats okay */
- ast_format_copy(ast_channel_readformat(transferee), ast_channel_readformat(chan1));
- ast_format_copy(ast_channel_writeformat(transferee), ast_channel_writeformat(chan1));
-
- /* Prepare for taking over the channel */
- if (ast_channel_masquerade(transferee, chan1)) {
- ast_hangup(transferee);
- ast_hangup(transferer);
- ast_free(d->park_exten);
- ast_free(d->park_context);
- ast_free(d);
- return -1;
- }
-
- /* Setup the extensions and such */
- ast_channel_context_set(transferee, ast_channel_context(chan1));
- ast_channel_exten_set(transferee, ast_channel_exten(chan1));
- ast_channel_priority_set(transferee, ast_channel_priority(chan1));
-
- ast_do_masquerade(transferee);
-
- /* We make a clone of the peer channel too, so we can play
- back the announcement */
-
- /* Make formats okay */
- ast_format_copy(ast_channel_readformat(transferer), ast_channel_readformat(chan2));
- ast_format_copy(ast_channel_writeformat(transferer), ast_channel_writeformat(chan2));
- ast_channel_parkinglot_set(transferer, ast_channel_parkinglot(chan2));
-
- /* Prepare for taking over the channel */
- if (ast_channel_masquerade(transferer, chan2)) {
- ast_hangup(transferer);
- ast_free(d->park_exten);
- ast_free(d->park_context);
- ast_free(d);
- return -1;
- }
-
- /* Setup the extensions and such */
- ast_channel_context_set(transferer, ast_channel_context(chan2));
- ast_channel_exten_set(transferer, ast_channel_exten(chan2));
- ast_channel_priority_set(transferer, ast_channel_priority(chan2));
-
- ast_do_masquerade(transferer);
-
- /* Save original request for followup */
- copy_request(&d->req, req);
- d->chan1 = transferee; /* Transferee */
- d->chan2 = transferer; /* Transferer */
- d->seqno = seqno;
- if (ast_pthread_create_detached_background(&th, NULL, sip_park_thread, d) < 0) {
- /* Could not start thread */
- deinit_req(&d->req);
- ast_free(d->park_exten);
- ast_free(d->park_context);
- ast_free(d); /* We don't need it anymore. If thread is created, d will be free'd
- by sip_park_thread() */
- return -1;
- }
- return 0;
-}
-
-
/*! \brief SIP pickup support function
* Starts in a new thread, then pickup the call
*/
@@ -26170,6 +26013,10 @@ static void parse_oli(struct sip_request *req, struct ast_channel *chan)
* If this function is successful, only the transferer pvt lock will remain on return. Setting nounlock indicates
* to handle_request_do() that the pvt's owner it locked does not require an unlock.
*/
+
+/* XXX XXX XXX XXX XXX XXX
+ * This function is COMPLETELY broken at the moment. It *will* crash if called
+ */
static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, uint32_t seqno, int *nounlock)
{
struct sip_dual target; /* Chan 1: Call from tranferer to Asterisk */
@@ -26370,6 +26217,44 @@ static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *
return 1;
}
+/*!
+ * Data to set on a channel that runs dialplan
+ * at the completion of a blind transfer
+ */
+struct blind_transfer_cb_data {
+ /*! Contents of the REFER's Referred-by header */
+ const char *referred_by;
+ /*! Domain of the URI in the REFER's Refer-To header */
+ const char *domain;
+ /*! Contents of what to place in a Replaces header of an INVITE */
+ const char *replaces;
+ /*! Redirecting information to set on the channel */
+ struct ast_party_redirecting redirecting;
+ /*! Parts of the redirecting structure that are to be updated */
+ struct ast_set_party_redirecting update_redirecting;
+};
+
+/*!
+ * \internal
+ * \brief Callback called on new outbound channel during blind transfer
+ *
+ * We use this opportunity to populate the channel with data from the REFER
+ * so that, if necessary, we can include proper information on any new INVITE
+ * we may send out.
+ *
+ * \param chan The new outbound channel
+ * \user_data A blind_transfer_cb_data struct
+ */
+static void blind_transfer_cb(struct ast_channel *chan, void *user_data)
+{
+ struct blind_transfer_cb_data *cb_data = user_data;
+
+ pbx_builtin_setvar_helper(chan, "SIPTRANSFER", "yes");
+ pbx_builtin_setvar_helper(chan, "SIPTRANSFER_REFERER", cb_data->referred_by);
+ pbx_builtin_setvar_helper(chan, "SIPTRANSFER_REPLACES", cb_data->replaces);
+ pbx_builtin_setvar_helper(chan, "SIPDOMAIN", cb_data->domain);
+ ast_channel_update_redirecting(chan, &cb_data->redirecting, &cb_data->update_redirecting);
+}
/*! \brief Handle incoming REFER request */
/*! \page SIP_REFER SIP transfer Support (REFER)
@@ -26436,22 +26321,13 @@ static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *
*/
static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, uint32_t seqno, int *nounlock)
{
- /*!
- * Chan1: Call between asterisk and transferer
- * Chan2: Call between asterisk and transferee
- */
- struct sip_dual current = { 0, };
- struct ast_channel *chans[2] = { 0, };
char *refer_to = NULL;
- char *refer_to_domain = NULL;
char *refer_to_context = NULL;
- char *referred_by = NULL;
- char *callid = NULL;
- int localtransfer = 0;
- int attendedtransfer = 0;
int res = 0;
- struct ast_party_redirecting redirecting;
- struct ast_set_party_redirecting update_redirecting;
+ struct blind_transfer_cb_data cb_data;
+ enum ast_transfer_result transfer_res;
+ RAII_VAR(struct ast_channel *, transferer, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_str *, replaces_str, NULL, ast_free_ptr);
if (req->debug) {
ast_verbose("Call %s got a SIP call transfer from %s: (REFER)!\n",
@@ -26469,8 +26345,7 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, uint
sip_alreadygone(p);
pvt_set_needdestroy(p, "outside of dialog");
}
- res = 0;
- goto handle_refer_cleanup;
+ return 0;
}
/* Check if transfer is allowed from this device */
@@ -26479,24 +26354,21 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, uint
transmit_response(p, "603 Declined (policy)", req);
append_history(p, "Xfer", "Refer failed. Allowtransfer == closed.");
/* Do not destroy SIP session */
- res = 0;
- goto handle_refer_cleanup;
+ return 0;
}
if (!req->ignore && ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
/* Already have a pending REFER */
transmit_response(p, "491 Request pending", req);
append_history(p, "Xfer", "Refer failed. Request pending.");
- res = 0;
- goto handle_refer_cleanup;
+ return 0;
}
/* Allocate memory for call transfer data */
if (!p->refer && !sip_refer_alloc(p)) {
transmit_response(p, "500 Internal Server Error", req);
append_history(p, "Xfer", "Refer failed. Memory allocation error.");
- res = -3;
- goto handle_refer_cleanup;
+ return -3;
}
res = get_refer_info(p, req); /* Extract headers */
@@ -26530,9 +26402,9 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, uint
}
break;
}
- res = 0;
- goto handle_refer_cleanup;
+ return 0;
}
+
if (ast_strlen_zero(p->context)) {
ast_string_field_set(p, context, sip_cfg.default_context);
}
@@ -26553,70 +26425,16 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, uint
/* Is this a repeat of a current request? Ignore it */
/* Don't know what else to do right now. */
if (req->ignore) {
- goto handle_refer_cleanup;
- }
-
- /* If this is a blind transfer, we have the following
- channels to work with:
- - chan1, chan2: The current call between transferer and transferee (2 channels)
- - target_channel: A new call from the transferee to the target (1 channel)
- We need to stay tuned to what happens in order to be able
- to bring back the call to the transferer */
-
- /* If this is a attended transfer, we should have all call legs within reach:
- - chan1, chan2: The call between the transferer and transferee (2 channels)
- - target_channel, targetcall_pvt: The call between the transferer and the target (2 channels)
- We want to bridge chan2 with targetcall_pvt!
-
- The replaces call id in the refer message points
- to the call leg between Asterisk and the transferer.
- So we need to connect the target and the transferee channel
- and hangup the two other channels silently
-
- If the target is non-local, the call ID could be on a remote
- machine and we need to send an INVITE with replaces to the
- target. We basically handle this as a blind transfer
- and let the sip_call function catch that we need replaces
- header in the INVITE.
- */
+ return 0;
+ }
/* Get the transferer's channel */
- chans[0] = current.chan1 = p->owner;
-
- /* Find the other part of the bridge (2) - transferee */
- chans[1] = current.chan2 = ast_bridged_channel(current.chan1);
-
- ast_channel_ref(current.chan1);
- if (current.chan2) {
- ast_channel_ref(current.chan2);
- }
+ transferer = ast_channel_ref(p->owner);
if (sipdebug) {
- ast_debug(3, "SIP %s transfer: Transferer channel %s, transferee channel %s\n",
+ ast_debug(3, "SIP %s transfer: Transferer channel %s\n",
p->refer->attendedtransfer ? "attended" : "blind",
- ast_channel_name(current.chan1),
- current.chan2 ? ast_channel_name(current.chan2) : "<none>");
- }
-
- if (!current.chan2 && !p->refer->attendedtransfer) {
- /* No bridged channel, propably IVR or echo or similar... */
- /* Guess we should masquerade or something here */
- /* Until we figure it out, refuse transfer of such calls */
- if (sipdebug) {
- ast_debug(3, "Refused SIP transfer on non-bridged channel.\n");
- }
- p->refer->status = REFER_FAILED;
- append_history(p, "Xfer", "Refer failed. Non-bridged channel.");
- transmit_response(p, "603 Declined", req);
- res = -1;
- goto handle_refer_cleanup;
- }
-
- if (current.chan2) {
- if (sipdebug) {
- ast_debug(4, "Got SIP transfer, applying to bridged peer '%s'\n", ast_channel_name(current.chan2));
- }
- ast_queue_control(current.chan1, AST_CONTROL_UNHOLD);
+ ast_channel_name(transferer));
}
ast_set_flag(&p->flags[0], SIP_GOTREFER);
@@ -26627,8 +26445,9 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, uint
/* Attended transfer: Find all call legs and bridge transferee with target*/
if (p->refer->attendedtransfer) {
/* both p and p->owner _MUST_ be locked while calling local_attended_transfer */
- if ((res = local_attended_transfer(p, &current, req, seqno, nounlock))) {
- goto handle_refer_cleanup; /* We're done with the transfer */
+ if ((res = local_attended_transfer(p, NULL, req, seqno, nounlock))) {
+ ast_clear_flag(&p->flags[0], SIP_GOTREFER);
+ return res;
}
/* Fall through for remote transfers that we did not find locally */
if (sipdebug) {
@@ -26639,210 +26458,74 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, uint
/* Copy data we can not safely access after letting the pvt lock go. */
refer_to = ast_strdupa(p->refer->refer_to);
- refer_to_domain = ast_strdupa(p->refer->refer_to_domain);
refer_to_context = ast_strdupa(p->refer->refer_to_context);
- referred_by = ast_strdupa(p->refer->referred_by);
- callid = ast_strdupa(p->callid);
- localtransfer = p->refer->localtransfer;
- attendedtransfer = p->refer->attendedtransfer;
-
- if (!*nounlock) {
- ast_channel_unlock(p->owner);
- *nounlock = 1;
- }
- sip_pvt_unlock(p);
-
- /* Parking a call. DO NOT hold any locks while calling ast_parking_ext_valid() */
- if (localtransfer && ast_parking_ext_valid(refer_to, current.chan1, refer_to_context)) {
- sip_pvt_lock(p);
- ast_clear_flag(&p->flags[0], SIP_GOTREFER);
- p->refer->status = REFER_200OK;
- append_history(p, "Xfer", "REFER to call parking.");
- sip_pvt_unlock(p);
- ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans,
- "TransferMethod: SIP\r\n"
- "TransferType: Blind\r\n"
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "SIP-Callid: %s\r\n"
- "TargetChannel: %s\r\n"
- "TargetUniqueid: %s\r\n"
- "TransferExten: %s\r\n"
- "Transfer2Parking: Yes\r\n",
- ast_channel_name(current.chan1),
- ast_channel_uniqueid(current.chan1),
- callid,
- ast_channel_name(current.chan2),
- ast_channel_uniqueid(current.chan2),
- refer_to);
+ ast_party_redirecting_init(&cb_data.redirecting);
+ memset(&cb_data.update_redirecting, 0, sizeof(cb_data.update_redirecting));
+ change_redirecting_information(p, req, &cb_data.redirecting, &cb_data.update_redirecting, 0);
- if (sipdebug) {
- ast_debug(4, "SIP transfer to parking: trying to park %s. Parked by %s\n", ast_channel_name(current.chan2), ast_channel_name(current.chan1));
- }
+ cb_data.domain = ast_strdupa(p->refer->refer_to_domain);
+ cb_data.referred_by = ast_strdupa(p->refer->referred_by);
- /* DO NOT hold any locks while calling sip_park */
- if (sip_park(current.chan2, current.chan1, req, seqno, refer_to, refer_to_context)) {
- sip_pvt_lock(p);
- transmit_notify_with_sipfrag(p, seqno, "500 Internal Server Error", TRUE);
- } else {
- sip_pvt_lock(p);
+ if (!ast_strlen_zero(p->refer->replaces_callid)) {
+ replaces_str = ast_str_create(128);
+ if (!replaces_str) {
+ ast_log(LOG_NOTICE, "Unable to create Replaces string for remote attended transfer. Transfer failed\n");
+ ast_clear_flag(&p->flags[0], SIP_GOTREFER);
+ ast_party_redirecting_free(&cb_data.redirecting);
+ return -1;
}
- goto handle_refer_cleanup;
- }
-
- /* Blind transfers and remote attended xfers.
- * Locks should not be held while calling pbx_builtin_setvar_helper. This function
- * locks the channel being passed into it.*/
- if (current.chan1 && current.chan2) {
- ast_debug(3, "chan1->name: %s\n", ast_channel_name(current.chan1));
- pbx_builtin_setvar_helper(current.chan1, "BLINDTRANSFER", ast_channel_name(current.chan2));
+ ast_str_append(&replaces_str, 0, "%s%s%s%s%s", p->refer->replaces_callid,
+ !ast_strlen_zero(p->refer->replaces_callid_totag) ? ";to-tag=" : "",
+ S_OR(p->refer->replaces_callid_totag, ""),
+ !ast_strlen_zero(p->refer->replaces_callid_fromtag) ? ";from-tag=" : "",
+ S_OR(p->refer->replaces_callid_fromtag, ""));
+ cb_data.replaces = ast_str_buffer(replaces_str);
+ } else {
+ cb_data.replaces = NULL;
}
- if (current.chan2) {
- pbx_builtin_setvar_helper(current.chan2, "BLINDTRANSFER", ast_channel_name(current.chan1));
- pbx_builtin_setvar_helper(current.chan2, "SIPDOMAIN", refer_to_domain);
- pbx_builtin_setvar_helper(current.chan2, "SIPTRANSFER", "yes");
- /* One for the new channel */
- pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER", "yes");
- /* Attended transfer to remote host, prepare headers for the INVITE */
- if (!ast_strlen_zero(referred_by)) {
- pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REFERER", referred_by);
- }
-
- /* When a call is transferred to voicemail from a Digium phone, there may be
- * a Diversion header present in the REFER with an appropriate reason parameter
- * set. We need to update the redirecting information appropriately.
- */
- ast_channel_lock(p->owner);
- sip_pvt_lock(p);
- ast_party_redirecting_init(&redirecting);
- memset(&update_redirecting, 0, sizeof(update_redirecting));
- change_redirecting_information(p, req, &redirecting, &update_redirecting, FALSE);
-
- /* Do not hold the pvt lock during a call that causes an indicate or an async_goto.
- * Those functions lock channels which will invalidate locking order if the pvt lock
- * is held.*/
- sip_pvt_unlock(p);
+ if (!*nounlock) {
ast_channel_unlock(p->owner);
- ast_channel_update_redirecting(current.chan2, &redirecting, &update_redirecting);
- ast_party_redirecting_free(&redirecting);
+ *nounlock = 1;
}
+ sip_pvt_unlock(p);
+ transfer_res = ast_bridge_transfer_blind(transferer, refer_to, refer_to_context, blind_transfer_cb, &cb_data);
sip_pvt_lock(p);
- /* Generate a Replaces string to be used in the INVITE during attended transfer */
- if (!ast_strlen_zero(p->refer->replaces_callid)) {
- char tempheader[SIPBUFSIZE];
- snprintf(tempheader, sizeof(tempheader), "%s%s%s%s%s", p->refer->replaces_callid,
- p->refer->replaces_callid_totag ? ";to-tag=" : "",
- p->refer->replaces_callid_totag,
- p->refer->replaces_callid_fromtag ? ";from-tag=" : "",
- p->refer->replaces_callid_fromtag);
-
- if (current.chan2) {
- sip_pvt_unlock(p);
- pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REPLACES", tempheader);
- sip_pvt_lock(p);
- }
- }
-
- /* Connect the call */
- /* FAKE ringing if not attended transfer */
- if (!p->refer->attendedtransfer) {
- transmit_notify_with_sipfrag(p, seqno, "180 Ringing", FALSE);
- }
-
- /* For blind transfer, this will lead to a new call */
- /* For attended transfer to remote host, this will lead to
- a new SIP call with a replaces header, if the dial plan allows it
- */
- if (!current.chan2) {
- /* We have no bridge, so we're talking with Asterisk somehow */
- /* We need to masquerade this call */
- /* What to do to fix this situation:
- * Set up the new call in a new channel
- * Let the new channel masq into this channel
- Please add that code here :-)
- */
+ switch (transfer_res) {
+ case AST_BRIDGE_TRANSFER_INVALID:
+ res = -1;
p->refer->status = REFER_FAILED;
transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable (can't handle one-legged xfers)", TRUE);
- ast_clear_flag(&p->flags[0], SIP_GOTREFER);
append_history(p, "Xfer", "Refer failed (only bridged calls).");
+ break;
+ case AST_BRIDGE_TRANSFER_NOT_PERMITTED:
res = -1;
- goto handle_refer_cleanup;
- }
- ast_set_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */
-
- sip_pvt_unlock(p);
-
- /* For blind transfers, move the call to the new extensions. For attended transfers on multiple
- * servers - generate an INVITE with Replaces. Either way, let the dial plan decided
- * indicate before masquerade so the indication actually makes it to the real channel
- * when using local channels with MOH passthru */
- ast_indicate(current.chan2, AST_CONTROL_UNHOLD);
- res = ast_async_goto(current.chan2, refer_to_context, refer_to, 1);
-
- if (!res) {
- ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans,
- "TransferMethod: SIP\r\n"
- "TransferType: Blind\r\n"
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "SIP-Callid: %s\r\n"
- "TargetChannel: %s\r\n"
- "TargetUniqueid: %s\r\n"
- "TransferExten: %s\r\n"
- "TransferContext: %s\r\n",
- ast_channel_name(current.chan1),
- ast_channel_uniqueid(current.chan1),
- callid,
- ast_channel_name(current.chan2),
- ast_channel_uniqueid(current.chan2),
- refer_to,
- refer_to_context);
- /* Success - we have a new channel */
- ast_debug(3, "%s transfer succeeded. Telling transferer.\n", attendedtransfer? "Attended" : "Blind");
-
- /* XXX - what to we put in CEL 'extra' for attended transfers to external systems? NULL for now */
- ast_channel_lock(current.chan1);
- ast_cel_report_event(current.chan1, p->refer->attendedtransfer? AST_CEL_ATTENDEDTRANSFER : AST_CEL_BLINDTRANSFER, NULL, p->refer->attendedtransfer ? NULL : p->refer->refer_to, current.chan2);
- ast_channel_unlock(current.chan1);
-
- sip_pvt_lock(p);
- transmit_notify_with_sipfrag(p, seqno, "200 Ok", TRUE);
- if (p->refer->localtransfer) {
- p->refer->status = REFER_200OK;
- }
- if (p->owner) {
- ast_channel_hangupcause_set(p->owner, AST_CAUSE_NORMAL_CLEARING);
- }
- append_history(p, "Xfer", "Refer succeeded.");
- ast_clear_flag(&p->flags[0], SIP_GOTREFER);
- /* Do not hangup call, the other side do that when we say 200 OK */
- /* We could possibly implement a timer here, auto congestion */
- res = 0;
- } else {
- sip_pvt_lock(p);
- ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Don't delay hangup */
- ast_debug(3, "%s transfer failed. Resuming original call.\n", p->refer->attendedtransfer? "Attended" : "Blind");
- append_history(p, "Xfer", "Refer failed.");
- /* Failure of some kind */
p->refer->status = REFER_FAILED;
- transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable", TRUE);
- ast_clear_flag(&p->flags[0], SIP_GOTREFER);
+ transmit_notify_with_sipfrag(p, seqno, "403 Forbidden", TRUE);
+ append_history(p, "Xfer", "Refer failed (bridge does not permit transfers)");
+ break;
+ case AST_BRIDGE_TRANSFER_FAIL:
res = -1;
+ p->refer->status = REFER_FAILED;
+ transmit_notify_with_sipfrag(p, seqno, "500 Internal Server Error", TRUE);
+ append_history(p, "Xfer", "Refer failed (internal error)");
+ break;
+ case AST_BRIDGE_TRANSFER_SUCCESS:
+ res = 0;
+ p->refer->status = REFER_200OK;
+ transmit_notify_with_sipfrag(p, seqno, "200 OK", TRUE);
+ ast_set_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
+ append_history(p, "Xfer", "Refer succeeded.");
+ break;
+ default:
+ break;
}
-handle_refer_cleanup:
- if (current.chan1) {
- ast_channel_unref(current.chan1);
- }
- if (current.chan2) {
- ast_channel_unref(current.chan2);
- }
-
- /* Make sure we exit with the pvt locked */
+ ast_clear_flag(&p->flags[0], SIP_GOTREFER);
+ ast_party_redirecting_free(&cb_data.redirecting);
return res;
}
@@ -32928,7 +32611,7 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *i
/* Disable early RTP bridge */
if ((instance || vinstance || tinstance) &&
- !ast_bridged_channel(chan) &&
+ !ast_channel_is_bridged(chan) &&
!sip_cfg.directrtpsetup) {
sip_pvt_unlock(p);
ast_channel_unlock(chan);
@@ -35058,5 +34741,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Session Initiation Pr
.unload = unload_module,
.reload = reload,
.load_pri = AST_MODPRI_CHANNEL_DRIVER,
- .nonoptreq = "res_crypto,chan_local,res_http_websocket",
+ .nonoptreq = "res_crypto,res_http_websocket",
);
diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c
index b3951d5e4..cd194d5a8 100644
--- a/channels/chan_skinny.c
+++ b/channels/chan_skinny.c
@@ -1653,7 +1653,6 @@ static struct ast_channel_tech skinny_tech = {
.fixup = skinny_fixup,
.send_digit_begin = skinny_senddigit_begin,
.send_digit_end = skinny_senddigit_end,
- .bridge = ast_rtp_instance_bridge,
};
static int skinny_extensionstate_cb(char *context, char *id, struct ast_state_cb_info *info, void *data);
diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c
index dea796364..121e2f0b1 100644
--- a/channels/chan_unistim.c
+++ b/channels/chan_unistim.c
@@ -708,7 +708,6 @@ static struct ast_channel_tech unistim_tech = {
.send_digit_begin = unistim_senddigit_begin,
.send_digit_end = unistim_senddigit_end,
.send_text = unistim_sendtext,
- .bridge = ast_rtp_instance_bridge,
};
static void send_start_rtp(struct unistim_subchannel *);
@@ -5826,7 +5825,6 @@ static char *unistim_show_info(struct ast_cli_entry *e, int cmd, struct ast_cli_
struct unistim_line *line;
struct unistim_subchannel *sub;
struct unistimsession *s;
- struct ast_channel *tmp;
switch (cmd) {
case CLI_INIT:
@@ -5870,14 +5868,9 @@ static char *unistim_show_info(struct ast_cli_entry *e, int cmd, struct ast_cli_
if (!sub) {
continue;
}
- if (!sub->owner) {
- tmp = (void *) -42;
- } else {
- tmp = ast_channel_internal_bridged_channel(sub->owner);
- }
ast_cli(a->fd,
- "-->subtype=%s chan=%p rtp=%p bridge=%p line=%p alreadygone=%d softkey=%d\n",
- subtype_tostr(sub->subtype), sub->owner, sub->rtp, tmp, sub->parent,
+ "-->subtype=%s chan=%p rtp=%p line=%p alreadygone=%d softkey=%d\n",
+ subtype_tostr(sub->subtype), sub->owner, sub->rtp, sub->parent,
sub->alreadygone, sub->softkey);
}
AST_LIST_UNLOCK(&device->subs);
diff --git a/channels/chan_vpb.cc b/channels/chan_vpb.cc
index 58fc73f69..d1747cb06 100644
--- a/channels/chan_vpb.cc
+++ b/channels/chan_vpb.cc
@@ -2267,13 +2267,7 @@ static void *do_chanreads(void *pvt)
else
bridgerec = 0;
} else {
- ast_verb(5, "%s: chanreads: No native bridge.\n", p->dev);
- if (ast_channel_internal_bridged_channel(p->owner)) {
- ast_verb(5, "%s: chanreads: Got Asterisk bridge with [%s].\n", p->dev, ast_channel_name(ast_channel_internal_bridged_channel(p->owner)));
- bridgerec = 1;
- } else {
- bridgerec = 0;
- }
+ bridgerec = ast_channel_is_bridged(p->owner) ? 1 : 0;
}
/* if ((p->owner->_state != AST_STATE_UP) || !bridgerec) */