diff options
author | Richard Mudgett <rmudgett@digium.com> | 2013-05-21 18:00:22 +0000 |
---|---|---|
committer | Richard Mudgett <rmudgett@digium.com> | 2013-05-21 18:00:22 +0000 |
commit | 3d63833bd6c869b7efa383e8dea14be1a6eff998 (patch) | |
tree | 34957dd051b8f67c7cc58a510e24ee3873a61ad4 /channels | |
parent | e1e1cc2deefb92f8b43825f1f34e619354737842 (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.c | 4 | ||||
-rw-r--r-- | channels/chan_bridge.c | 235 | ||||
-rw-r--r-- | channels/chan_dahdi.c | 1 | ||||
-rw-r--r-- | channels/chan_gulp.c | 1 | ||||
-rw-r--r-- | channels/chan_h323.c | 1 | ||||
-rw-r--r-- | channels/chan_iax2.c | 172 | ||||
-rw-r--r-- | channels/chan_jingle.c | 1 | ||||
-rw-r--r-- | channels/chan_local.c | 1452 | ||||
-rw-r--r-- | channels/chan_mgcp.c | 102 | ||||
-rw-r--r-- | channels/chan_misdn.c | 1 | ||||
-rw-r--r-- | channels/chan_motif.c | 1 | ||||
-rw-r--r-- | channels/chan_sip.c | 539 | ||||
-rw-r--r-- | channels/chan_skinny.c | 1 | ||||
-rw-r--r-- | channels/chan_unistim.c | 11 | ||||
-rw-r--r-- | channels/chan_vpb.cc | 8 |
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, ¤t, 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) */ |