[asterisk-commits] mmichelson: branch mmichelson/atxfer_features r390567 - in /team/mmichelson/a...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Wed Jun 5 14:55:30 CDT 2013
Author: mmichelson
Date: Wed Jun 5 14:55:29 2013
New Revision: 390567
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=390567
Log:
Move attended transfer support into bridging_basic.c
Because there is some degree of shared code between attended and blind transfers,
there are currently copies of the shared code in bridging_basic.c. The easiest way
to fix this would be to move blind transfer support into bridging_basic.c as well.
Modified:
team/mmichelson/atxfer_features/bridges/bridge_builtin_features.c
team/mmichelson/atxfer_features/main/bridging_basic.c
Modified: team/mmichelson/atxfer_features/bridges/bridge_builtin_features.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/atxfer_features/bridges/bridge_builtin_features.c?view=diff&rev=390567&r1=390566&r2=390567
==============================================================================
--- team/mmichelson/atxfer_features/bridges/bridge_builtin_features.c (original)
+++ team/mmichelson/atxfer_features/bridges/bridge_builtin_features.c Wed Jun 5 14:55:29 2013
@@ -117,35 +117,6 @@
ast_channel_unlock(caller);
}
-/*! \brief Helper function that creates an outgoing channel and returns it immediately */
-static struct ast_channel *dial_transfer(struct ast_channel *caller, const char *exten, const char *context)
-{
- char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1];
- struct ast_channel *chan;
- int cause;
-
- /* Fill the variable with the extension and context we want to call */
- snprintf(destination, sizeof(destination), "%s@%s", exten, context);
-
- /* Now we request a local channel to prepare to call the destination */
- chan = ast_request("Local", ast_channel_nativeformats(caller), caller, destination,
- &cause);
- if (!chan) {
- return NULL;
- }
-
- /* Before we actually dial out let's inherit appropriate information. */
- copy_caller_data(chan, caller);
-
- /* Since the above worked fine now we actually call it and return the channel */
- if (ast_call(chan, destination, 0)) {
- ast_hangup(chan);
- return NULL;
- }
-
- return chan;
-}
-
/*!
* \internal
* \brief Determine the transfer context to use.
@@ -219,227 +190,6 @@
bridge_channel->chan) != AST_BRIDGE_TRANSFER_SUCCESS &&
!ast_strlen_zero(goto_on_blindxfr)) {
ast_after_bridge_goto_discard(bridge_channel->chan);
- }
-
- return 0;
-}
-
-/*! Attended transfer code */
-enum atxfer_code {
- /*! Party C hungup or other reason to abandon the transfer. */
- ATXFER_INCOMPLETE,
- /*! Transfer party C to party A. */
- ATXFER_COMPLETE,
- /*! Turn the transfer into a threeway call. */
- ATXFER_THREEWAY,
- /*! Hangup party C and return party B to the bridge. */
- ATXFER_ABORT,
-};
-
-/*! \brief Attended transfer feature to complete transfer */
-static int attended_transfer_complete(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
- enum atxfer_code *transfer_code = hook_pvt;
-
- *transfer_code = ATXFER_COMPLETE;
- ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
- return 0;
-}
-
-/*! \brief Attended transfer feature to turn it into a threeway call */
-static int attended_transfer_threeway(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
- enum atxfer_code *transfer_code = hook_pvt;
-
- *transfer_code = ATXFER_THREEWAY;
- ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
- return 0;
-}
-
-/*! \brief Attended transfer feature to abort transfer */
-static int attended_transfer_abort(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
- enum atxfer_code *transfer_code = hook_pvt;
-
- *transfer_code = ATXFER_ABORT;
- ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
- return 0;
-}
-
-/*! \brief Internal built in feature for attended transfers */
-static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
- char exten[AST_MAX_EXTENSION] = "";
- struct ast_channel *peer;
- struct ast_bridge *attended_bridge;
- struct ast_bridge_features caller_features;
- int xfer_failed;
- struct ast_bridge_features_attended_transfer *attended_transfer = hook_pvt;
- const char *context;
- enum atxfer_code transfer_code = ATXFER_INCOMPLETE;
- const char *atxfer_abort;
- const char *atxfer_threeway;
- const char *atxfer_complete;
- const char *fail_sound;
- RAII_VAR(struct ast_features_xfer_config *, xfer_cfg, NULL, ao2_cleanup);
-
- ast_bridge_channel_write_hold(bridge_channel, NULL);
-
- bridge = ast_bridge_channel_merge_inhibit(bridge_channel, +1);
-
- ast_channel_lock(bridge_channel->chan);
- context = ast_strdupa(get_transfer_context(bridge_channel->chan,
- attended_transfer ? attended_transfer->context : NULL));
- xfer_cfg = ast_get_chan_features_xfer_config(bridge_channel->chan);
- if (!xfer_cfg) {
- ast_log(LOG_ERROR, "Unable to get transfer configuration options\n");
- ast_channel_unlock(bridge_channel->chan);
- return 0;
- }
- if (attended_transfer) {
- atxfer_abort = ast_strdupa(S_OR(attended_transfer->abort, xfer_cfg->atxferabort));
- atxfer_threeway = ast_strdupa(S_OR(attended_transfer->threeway, xfer_cfg->atxferthreeway));
- atxfer_complete = ast_strdupa(S_OR(attended_transfer->complete, xfer_cfg->atxfercomplete));
- } else {
- atxfer_abort = ast_strdupa(xfer_cfg->atxferabort);
- atxfer_threeway = ast_strdupa(xfer_cfg->atxferthreeway);
- atxfer_complete = ast_strdupa(xfer_cfg->atxfercomplete);
- }
- fail_sound = ast_strdupa(xfer_cfg->xferfailsound);
- ast_channel_unlock(bridge_channel->chan);
-
- /* Grab the extension to transfer to */
- if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
- ast_bridge_merge_inhibit(bridge, -1);
- ao2_ref(bridge, -1);
- ast_bridge_channel_write_unhold(bridge_channel);
- return 0;
- }
-
- /* Get a channel that is the destination we wish to call */
- peer = dial_transfer(bridge_channel->chan, exten, context);
- if (!peer) {
- ast_bridge_merge_inhibit(bridge, -1);
- ao2_ref(bridge, -1);
- ast_stream_and_wait(bridge_channel->chan, fail_sound, AST_DIGIT_NONE);
- ast_bridge_channel_write_unhold(bridge_channel);
- return 0;
- }
-
-/* BUGBUG bridging API features does not support the features.conf atxfer bounce between C & B channels */
- /* Setup a DTMF menu to control the transfer. */
- if (ast_bridge_features_init(&caller_features)
- || ast_bridge_hangup_hook(&caller_features,
- attended_transfer_complete, &transfer_code, NULL, 0)
- || ast_bridge_dtmf_hook(&caller_features, atxfer_abort,
- attended_transfer_abort, &transfer_code, NULL, 0)
- || ast_bridge_dtmf_hook(&caller_features, atxfer_complete,
- attended_transfer_complete, &transfer_code, NULL, 0)
- || ast_bridge_dtmf_hook(&caller_features, atxfer_threeway,
- attended_transfer_threeway, &transfer_code, NULL, 0)) {
- ast_bridge_features_cleanup(&caller_features);
- ast_hangup(peer);
- ast_bridge_merge_inhibit(bridge, -1);
- ao2_ref(bridge, -1);
- ast_stream_and_wait(bridge_channel->chan, fail_sound, AST_DIGIT_NONE);
- ast_bridge_channel_write_unhold(bridge_channel);
- return 0;
- }
-
- /* Create a bridge to use to talk to the person we are calling */
- attended_bridge = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_1TO1MIX,
- AST_BRIDGE_FLAG_DISSOLVE_HANGUP);
- if (!attended_bridge) {
- ast_bridge_features_cleanup(&caller_features);
- ast_hangup(peer);
- ast_bridge_merge_inhibit(bridge, -1);
- ao2_ref(bridge, -1);
- ast_stream_and_wait(bridge_channel->chan, fail_sound, AST_DIGIT_NONE);
- ast_bridge_channel_write_unhold(bridge_channel);
- return 0;
- }
- ast_bridge_merge_inhibit(attended_bridge, +1);
-
- /* This is how this is going down, we are imparting the channel we called above into this bridge first */
-/* BUGBUG we should impart the peer as an independent and move it to the original bridge. */
- if (ast_bridge_impart(attended_bridge, peer, NULL, NULL, 0)) {
- ast_bridge_destroy(attended_bridge);
- ast_bridge_features_cleanup(&caller_features);
- ast_hangup(peer);
- ast_bridge_merge_inhibit(bridge, -1);
- ao2_ref(bridge, -1);
- ast_stream_and_wait(bridge_channel->chan, fail_sound, AST_DIGIT_NONE);
- ast_bridge_channel_write_unhold(bridge_channel);
- return 0;
- }
-
- /*
- * For the caller we want to join the bridge in a blocking
- * fashion so we don't spin around in this function doing
- * nothing while waiting.
- */
- ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features, NULL, 0);
-
-/*
- * BUGBUG there is a small window where the channel does not point to the bridge_channel.
- *
- * This window is expected to go away when atxfer is redesigned
- * to fully support existing functionality. There will be one
- * and only one ast_bridge_channel structure per channel.
- */
- /* Point the channel back to the original bridge and bridge_channel. */
- ast_bridge_channel_lock(bridge_channel);
- ast_channel_lock(bridge_channel->chan);
- ast_channel_internal_bridge_channel_set(bridge_channel->chan, bridge_channel);
- ast_channel_internal_bridge_set(bridge_channel->chan, bridge_channel->bridge);
- ast_channel_unlock(bridge_channel->chan);
- ast_bridge_channel_unlock(bridge_channel);
-
- /* Wait for peer thread to exit bridge and die. */
- if (!ast_autoservice_start(bridge_channel->chan)) {
- ast_bridge_depart(peer);
- ast_autoservice_stop(bridge_channel->chan);
- } else {
- ast_bridge_depart(peer);
- }
-
- /* Now that all channels are out of it we can destroy the bridge and the feature structures */
- ast_bridge_destroy(attended_bridge);
- ast_bridge_features_cleanup(&caller_features);
-
- xfer_failed = -1;
- switch (transfer_code) {
- case ATXFER_INCOMPLETE:
- /* Peer hungup */
- break;
- case ATXFER_COMPLETE:
- /* The peer takes our place in the bridge. */
- ast_bridge_channel_write_unhold(bridge_channel);
- ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
- xfer_failed = ast_bridge_impart(bridge_channel->bridge, peer, bridge_channel->chan, NULL, 1);
- break;
- case ATXFER_THREEWAY:
- /*
- * Transferer wants to convert to a threeway call.
- *
- * Just impart the peer onto the bridge and have us return to it
- * as normal.
- */
- ast_bridge_channel_write_unhold(bridge_channel);
- xfer_failed = ast_bridge_impart(bridge_channel->bridge, peer, NULL, NULL, 1);
- break;
- case ATXFER_ABORT:
- /* Transferer decided not to transfer the call after all. */
- break;
- }
- ast_bridge_merge_inhibit(bridge, -1);
- ao2_ref(bridge, -1);
- if (xfer_failed) {
- ast_hangup(peer);
- if (!ast_check_hangup_locked(bridge_channel->chan)) {
- ast_stream_and_wait(bridge_channel->chan, fail_sound, AST_DIGIT_NONE);
- }
- ast_bridge_channel_write_unhold(bridge_channel);
}
return 0;
@@ -465,7 +215,6 @@
static int load_module(void)
{
ast_bridge_features_register(AST_BRIDGE_BUILTIN_BLINDTRANSFER, feature_blind_transfer, NULL);
- ast_bridge_features_register(AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER, feature_attended_transfer, NULL);
ast_bridge_features_register(AST_BRIDGE_BUILTIN_HANGUP, feature_hangup, NULL);
/* Bump up our reference count so we can't be unloaded */
Modified: team/mmichelson/atxfer_features/main/bridging_basic.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/atxfer_features/main/bridging_basic.c?view=diff&rev=390567&r1=390566&r2=390567
==============================================================================
--- team/mmichelson/atxfer_features/main/bridging_basic.c (original)
+++ team/mmichelson/atxfer_features/main/bridging_basic.c Wed Jun 5 14:55:29 2013
@@ -37,6 +37,10 @@
#include "asterisk/bridging.h"
#include "asterisk/bridging_basic.h"
#include "asterisk/astobj2.h"
+#include "asterisk/features_config.h"
+#include "asterisk/pbx.h"
+#include "asterisk/file.h"
+#include "asterisk/app.h"
/* ------------------------------------------------------------------- */
@@ -187,14 +191,6 @@
bridge->personality = &bridge_normal_personality;
bridge = ast_bridge_register(bridge);
return bridge;
-}
-
-void ast_bridging_init_basic(void)
-{
- /* Setup bridge basic subclass v_table. */
- ast_bridge_basic_v_table = ast_bridge_base_v_table;
- ast_bridge_basic_v_table.name = "basic";
- ast_bridge_basic_v_table.push = bridge_basic_push;
}
static int remove_hook(void *obj, void *arg, int flags)
@@ -255,3 +251,369 @@
bridge->personality = &bridge_normal_personality;
remove_hooks_on_personality_change(bridge);
}
+
+/*!
+ * \brief Helper function that presents dialtone and grabs extension
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+/* XXX This is a copy of a function in bridges/bridge_builtin_features.c. It likely should not
+ * exist in two places
+ */
+static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len, const char *context)
+{
+ int res;
+ int digit_timeout;
+ RAII_VAR(struct ast_features_xfer_config *, xfer_cfg, NULL, ao2_cleanup);
+
+ ast_channel_lock(chan);
+ xfer_cfg = ast_get_chan_features_xfer_config(chan);
+ if (!xfer_cfg) {
+ ast_log(LOG_ERROR, "Unable to get transfer configuration\n");
+ ast_channel_unlock(chan);
+ return -1;
+ }
+ digit_timeout = xfer_cfg->transferdigittimeout;
+ ast_channel_unlock(chan);
+
+ /* Play the simple "transfer" prompt out and wait */
+ res = ast_stream_and_wait(chan, "pbx-transfer", AST_DIGIT_ANY);
+ ast_stopstream(chan);
+ if (res < 0) {
+ /* Hangup or error */
+ return -1;
+ }
+ if (res) {
+ /* Store the DTMF digit that interrupted playback of the file. */
+ exten[0] = res;
+ }
+
+ /* Drop to dialtone so they can enter the extension they want to transfer to */
+ res = ast_app_dtget(chan, context, exten, exten_len, exten_len - 1, digit_timeout);
+ if (res < 0) {
+ /* Hangup or error */
+ res = -1;
+ } else if (!res) {
+ /* 0 for invalid extension dialed. */
+ if (ast_strlen_zero(exten)) {
+ ast_debug(1, "%s dialed no digits.\n", ast_channel_name(chan));
+ } else {
+ ast_debug(1, "%s dialed '%s@%s' does not exist.\n",
+ ast_channel_name(chan), exten, context);
+ }
+ ast_stream_and_wait(chan, "pbx-invalid", AST_DIGIT_NONE);
+ res = -1;
+ } else {
+ /* Dialed extension is valid. */
+ res = 0;
+ }
+ return res;
+}
+
+/* XXX This is a copy of a function in bridges/bridge_builtin_features.c. It likely should not
+ * exist in two places
+ */
+static void copy_caller_data(struct ast_channel *dest, struct ast_channel *caller)
+{
+ ast_channel_lock_both(caller, dest);
+ ast_connected_line_copy_from_caller(ast_channel_connected(dest), ast_channel_caller(caller));
+ ast_channel_inherit_variables(caller, dest);
+ ast_channel_datastore_inherit(caller, dest);
+ ast_channel_unlock(dest);
+ ast_channel_unlock(caller);
+}
+
+/*! \brief Helper function that creates an outgoing channel and returns it immediately */
+static struct ast_channel *dial_transfer(struct ast_channel *caller, const char *exten, const char *context)
+{
+ char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1];
+ struct ast_channel *chan;
+ int cause;
+
+ /* Fill the variable with the extension and context we want to call */
+ snprintf(destination, sizeof(destination), "%s@%s", exten, context);
+
+ /* Now we request a local channel to prepare to call the destination */
+ chan = ast_request("Local", ast_channel_nativeformats(caller), caller, destination,
+ &cause);
+ if (!chan) {
+ return NULL;
+ }
+
+ /* Before we actually dial out let's inherit appropriate information. */
+ copy_caller_data(chan, caller);
+
+ /* Since the above worked fine now we actually call it and return the channel */
+ if (ast_call(chan, destination, 0)) {
+ ast_hangup(chan);
+ return NULL;
+ }
+
+ return chan;
+}
+
+/*!
+ * \internal
+ * \brief Determine the transfer context to use.
+ * \since 12.0.0
+ *
+ * \param transferer Channel initiating the transfer.
+ * \param context User supplied context if available. May be NULL.
+ *
+ * \return The context to use for the transfer.
+ */
+/* XXX This is a copy of a function in bridges/bridge_builtin_features.c. It likely should not
+ * exist in two places
+ */
+static const char *get_transfer_context(struct ast_channel *transferer, const char *context)
+{
+ if (!ast_strlen_zero(context)) {
+ return context;
+ }
+ context = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT");
+ if (!ast_strlen_zero(context)) {
+ return context;
+ }
+ context = ast_channel_macrocontext(transferer);
+ if (!ast_strlen_zero(context)) {
+ return context;
+ }
+ context = ast_channel_context(transferer);
+ if (!ast_strlen_zero(context)) {
+ return context;
+ }
+ return "default";
+}
+
+/*! Attended transfer code */
+enum atxfer_code {
+ /*! Party C hungup or other reason to abandon the transfer. */
+ ATXFER_INCOMPLETE,
+ /*! Transfer party C to party A. */
+ ATXFER_COMPLETE,
+ /*! Turn the transfer into a threeway call. */
+ ATXFER_THREEWAY,
+ /*! Hangup party C and return party B to the bridge. */
+ ATXFER_ABORT,
+};
+
+/*! \brief Attended transfer feature to complete transfer */
+static int attended_transfer_complete(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+ enum atxfer_code *transfer_code = hook_pvt;
+
+ *transfer_code = ATXFER_COMPLETE;
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+ return 0;
+}
+
+/*! \brief Attended transfer feature to turn it into a threeway call */
+static int attended_transfer_threeway(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+ enum atxfer_code *transfer_code = hook_pvt;
+
+ *transfer_code = ATXFER_THREEWAY;
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+ return 0;
+}
+
+/*! \brief Attended transfer feature to abort transfer */
+static int attended_transfer_abort(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+ enum atxfer_code *transfer_code = hook_pvt;
+
+ *transfer_code = ATXFER_ABORT;
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+ return 0;
+}
+
+/*! \brief Internal built in feature for attended transfers */
+static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+ char exten[AST_MAX_EXTENSION] = "";
+ struct ast_channel *peer;
+ struct ast_bridge *attended_bridge;
+ struct ast_bridge_features caller_features;
+ int xfer_failed;
+ struct ast_bridge_features_attended_transfer *attended_transfer = hook_pvt;
+ const char *context;
+ enum atxfer_code transfer_code = ATXFER_INCOMPLETE;
+ const char *atxfer_abort;
+ const char *atxfer_threeway;
+ const char *atxfer_complete;
+ const char *fail_sound;
+ RAII_VAR(struct ast_features_xfer_config *, xfer_cfg, NULL, ao2_cleanup);
+
+ ast_bridge_channel_write_hold(bridge_channel, NULL);
+
+ bridge = ast_bridge_channel_merge_inhibit(bridge_channel, +1);
+
+ ast_channel_lock(bridge_channel->chan);
+ context = ast_strdupa(get_transfer_context(bridge_channel->chan,
+ attended_transfer ? attended_transfer->context : NULL));
+ xfer_cfg = ast_get_chan_features_xfer_config(bridge_channel->chan);
+ if (!xfer_cfg) {
+ ast_log(LOG_ERROR, "Unable to get transfer configuration options\n");
+ ast_channel_unlock(bridge_channel->chan);
+ return 0;
+ }
+ if (attended_transfer) {
+ atxfer_abort = ast_strdupa(S_OR(attended_transfer->abort, xfer_cfg->atxferabort));
+ atxfer_threeway = ast_strdupa(S_OR(attended_transfer->threeway, xfer_cfg->atxferthreeway));
+ atxfer_complete = ast_strdupa(S_OR(attended_transfer->complete, xfer_cfg->atxfercomplete));
+ } else {
+ atxfer_abort = ast_strdupa(xfer_cfg->atxferabort);
+ atxfer_threeway = ast_strdupa(xfer_cfg->atxferthreeway);
+ atxfer_complete = ast_strdupa(xfer_cfg->atxfercomplete);
+ }
+ fail_sound = ast_strdupa(xfer_cfg->xferfailsound);
+ ast_channel_unlock(bridge_channel->chan);
+
+ /* Grab the extension to transfer to */
+ if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
+ ast_bridge_merge_inhibit(bridge, -1);
+ ao2_ref(bridge, -1);
+ ast_bridge_channel_write_unhold(bridge_channel);
+ return 0;
+ }
+
+ /* Get a channel that is the destination we wish to call */
+ peer = dial_transfer(bridge_channel->chan, exten, context);
+ if (!peer) {
+ ast_bridge_merge_inhibit(bridge, -1);
+ ao2_ref(bridge, -1);
+ ast_stream_and_wait(bridge_channel->chan, fail_sound, AST_DIGIT_NONE);
+ ast_bridge_channel_write_unhold(bridge_channel);
+ return 0;
+ }
+
+/* BUGBUG bridging API features does not support the features.conf atxfer bounce between C & B channels */
+ /* Setup a DTMF menu to control the transfer. */
+ if (ast_bridge_features_init(&caller_features)
+ || ast_bridge_hangup_hook(&caller_features,
+ attended_transfer_complete, &transfer_code, NULL, 0)
+ || ast_bridge_dtmf_hook(&caller_features, atxfer_abort,
+ attended_transfer_abort, &transfer_code, NULL, 0)
+ || ast_bridge_dtmf_hook(&caller_features, atxfer_complete,
+ attended_transfer_complete, &transfer_code, NULL, 0)
+ || ast_bridge_dtmf_hook(&caller_features, atxfer_threeway,
+ attended_transfer_threeway, &transfer_code, NULL, 0)) {
+ ast_bridge_features_cleanup(&caller_features);
+ ast_hangup(peer);
+ ast_bridge_merge_inhibit(bridge, -1);
+ ao2_ref(bridge, -1);
+ ast_stream_and_wait(bridge_channel->chan, fail_sound, AST_DIGIT_NONE);
+ ast_bridge_channel_write_unhold(bridge_channel);
+ return 0;
+ }
+
+ /* Create a bridge to use to talk to the person we are calling */
+ attended_bridge = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_1TO1MIX,
+ AST_BRIDGE_FLAG_DISSOLVE_HANGUP);
+ if (!attended_bridge) {
+ ast_bridge_features_cleanup(&caller_features);
+ ast_hangup(peer);
+ ast_bridge_merge_inhibit(bridge, -1);
+ ao2_ref(bridge, -1);
+ ast_stream_and_wait(bridge_channel->chan, fail_sound, AST_DIGIT_NONE);
+ ast_bridge_channel_write_unhold(bridge_channel);
+ return 0;
+ }
+ ast_bridge_merge_inhibit(attended_bridge, +1);
+
+ /* This is how this is going down, we are imparting the channel we called above into this bridge first */
+/* BUGBUG we should impart the peer as an independent and move it to the original bridge. */
+ if (ast_bridge_impart(attended_bridge, peer, NULL, NULL, 0)) {
+ ast_bridge_destroy(attended_bridge);
+ ast_bridge_features_cleanup(&caller_features);
+ ast_hangup(peer);
+ ast_bridge_merge_inhibit(bridge, -1);
+ ao2_ref(bridge, -1);
+ ast_stream_and_wait(bridge_channel->chan, fail_sound, AST_DIGIT_NONE);
+ ast_bridge_channel_write_unhold(bridge_channel);
+ return 0;
+ }
+
+ /*
+ * For the caller we want to join the bridge in a blocking
+ * fashion so we don't spin around in this function doing
+ * nothing while waiting.
+ */
+ ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features, NULL, 0);
+
+/*
+ * BUGBUG there is a small window where the channel does not point to the bridge_channel.
+ *
+ * This window is expected to go away when atxfer is redesigned
+ * to fully support existing functionality. There will be one
+ * and only one ast_bridge_channel structure per channel.
+ */
+ /* Point the channel back to the original bridge and bridge_channel. */
+ ast_bridge_channel_lock(bridge_channel);
+ ast_channel_lock(bridge_channel->chan);
+ ast_channel_internal_bridge_channel_set(bridge_channel->chan, bridge_channel);
+ ast_channel_internal_bridge_set(bridge_channel->chan, bridge_channel->bridge);
+ ast_channel_unlock(bridge_channel->chan);
+ ast_bridge_channel_unlock(bridge_channel);
+
+ /* Wait for peer thread to exit bridge and die. */
+ if (!ast_autoservice_start(bridge_channel->chan)) {
+ ast_bridge_depart(peer);
+ ast_autoservice_stop(bridge_channel->chan);
+ } else {
+ ast_bridge_depart(peer);
+ }
+
+ /* Now that all channels are out of it we can destroy the bridge and the feature structures */
+ ast_bridge_destroy(attended_bridge);
+ ast_bridge_features_cleanup(&caller_features);
+
+ xfer_failed = -1;
+ switch (transfer_code) {
+ case ATXFER_INCOMPLETE:
+ /* Peer hungup */
+ break;
+ case ATXFER_COMPLETE:
+ /* The peer takes our place in the bridge. */
+ ast_bridge_channel_write_unhold(bridge_channel);
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+ xfer_failed = ast_bridge_impart(bridge_channel->bridge, peer, bridge_channel->chan, NULL, 1);
+ break;
+ case ATXFER_THREEWAY:
+ /*
+ * Transferer wants to convert to a threeway call.
+ *
+ * Just impart the peer onto the bridge and have us return to it
+ * as normal.
+ */
+ ast_bridge_channel_write_unhold(bridge_channel);
+ xfer_failed = ast_bridge_impart(bridge_channel->bridge, peer, NULL, NULL, 1);
+ break;
+ case ATXFER_ABORT:
+ /* Transferer decided not to transfer the call after all. */
+ break;
+ }
+ ast_bridge_merge_inhibit(bridge, -1);
+ ao2_ref(bridge, -1);
+ if (xfer_failed) {
+ ast_hangup(peer);
+ if (!ast_check_hangup_locked(bridge_channel->chan)) {
+ ast_stream_and_wait(bridge_channel->chan, fail_sound, AST_DIGIT_NONE);
+ }
+ ast_bridge_channel_write_unhold(bridge_channel);
+ }
+
+ return 0;
+}
+
+void ast_bridging_init_basic(void)
+{
+ /* Setup bridge basic subclass v_table. */
+ ast_bridge_basic_v_table = ast_bridge_base_v_table;
+ ast_bridge_basic_v_table.name = "basic";
+ ast_bridge_basic_v_table.push = bridge_basic_push;
+
+ ast_bridge_features_register(AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER, feature_attended_transfer, NULL);
+}
+
More information about the asterisk-commits
mailing list