[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