[svn-commits] mmichelson: trunk r395151 - in /trunk: bridges/ include/asterisk/ main/

SVN commits to the Digium repositories svn-commits at lists.digium.com
Tue Jul 23 10:28:22 CDT 2013


Author: mmichelson
Date: Tue Jul 23 10:28:11 2013
New Revision: 395151

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=395151
Log:
Make DTMF attended transfer support feature-complete.

This greatly modifies the operation of DTMF attended transfers so that
the full range of options from features.conf applies.

In addition, a new option has been added that allows for a transferer
to switch between bridges during a transfer before completing the
transfer.

(closes issue ASTERISK-21543)
reported by Matt Jordan

Review: https://reviewboard.asterisk.org/r/2654


Added:
    trunk/include/asterisk/bridging_internal.h   (with props)
Modified:
    trunk/bridges/bridge_builtin_features.c
    trunk/include/asterisk/bridging.h
    trunk/include/asterisk/bridging_features.h
    trunk/include/asterisk/bridging_roles.h
    trunk/include/asterisk/features_config.h
    trunk/include/asterisk/stasis_bridging.h
    trunk/main/bridging.c
    trunk/main/bridging_basic.c
    trunk/main/bridging_roles.c
    trunk/main/cel.c
    trunk/main/features.c
    trunk/main/features_config.c
    trunk/main/stasis_bridging.c

Modified: trunk/bridges/bridge_builtin_features.c
URL: http://svnview.digium.com/svn/asterisk/trunk/bridges/bridge_builtin_features.c?view=diff&rev=395151&r1=395150&r2=395151
==============================================================================
--- trunk/bridges/bridge_builtin_features.c (original)
+++ trunk/bridges/bridge_builtin_features.c Tue Jul 23 10:28:11 2013
@@ -54,421 +54,6 @@
 #include "asterisk/mixmonitor.h"
 #include "asterisk/audiohook.h"
 
-/*!
- * \brief Helper function that presents dialtone and grabs extension
- *
- * \retval 0 on success
- * \retval -1 on failure
- */
-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;
-}
-
-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;
-	}
-
-	/* Who is transferring the call. */
-	pbx_builtin_setvar_helper(chan, "TRANSFERERNAME", ast_channel_name(caller));
-
-	/* To work as an analog to BLINDTRANSFER */
-	pbx_builtin_setvar_helper(chan, "ATTENDEDTRANSFER", ast_channel_name(caller));
-
-	/* 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.
- */
-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";
-}
-
-static void blind_transfer_cb(struct ast_channel *new_channel, void *user_data,
-		enum ast_transfer_type transfer_type)
-{
-	struct ast_channel *transferer_channel = user_data;
-
-	if (transfer_type == AST_BRIDGE_TRANSFER_MULTI_PARTY) {
-		copy_caller_data(new_channel, transferer_channel);
-	}
-}
-
-/*! \brief Internal built in feature for blind transfers */
-static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
-	char exten[AST_MAX_EXTENSION] = "";
-	struct ast_bridge_features_blind_transfer *blind_transfer = hook_pvt;
-	const char *context;
-	char *goto_on_blindxfr;
-
-	ast_bridge_channel_write_hold(bridge_channel, NULL);
-
-	ast_channel_lock(bridge_channel->chan);
-	context = ast_strdupa(get_transfer_context(bridge_channel->chan,
-		blind_transfer ? blind_transfer->context : NULL));
-	goto_on_blindxfr = ast_strdupa(S_OR(pbx_builtin_getvar_helper(bridge_channel->chan,
-		"GOTO_ON_BLINDXFR"), ""));
-	ast_channel_unlock(bridge_channel->chan);
-
-	/* Grab the extension to transfer to */
-	if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
-		ast_bridge_channel_write_unhold(bridge_channel);
-		return 0;
-	}
-
-	if (!ast_strlen_zero(goto_on_blindxfr)) {
-		ast_debug(1, "After transfer, transferer %s goes to %s\n",
-				ast_channel_name(bridge_channel->chan), goto_on_blindxfr);
-		ast_after_bridge_set_go_on(bridge_channel->chan, NULL, NULL, 0, goto_on_blindxfr);
-	}
-
-	if (ast_bridge_transfer_blind(0, bridge_channel->chan, exten, context, blind_transfer_cb,
-			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 *complete_sound;
-	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);
-
-	/* Is there a courtesy sound to play to the peer? */
-	ast_channel_lock(bridge_channel->chan);
-	complete_sound = pbx_builtin_getvar_helper(bridge_channel->chan,
-		"ATTENDED_TRANSFER_COMPLETE_SOUND");
-	if (!ast_strlen_zero(complete_sound)) {
-		complete_sound = ast_strdupa(complete_sound);
-	} else {
-		complete_sound = NULL;
-	}
-	ast_channel_unlock(bridge_channel->chan);
-	if (complete_sound) {
-		pbx_builtin_setvar_helper(peer, "BRIDGE_PLAY_SOUND", complete_sound);
-	}
-
-	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;
-}
-
 enum set_touch_variables_res {
 	SET_TOUCH_SUCCESS,
 	SET_TOUCH_UNSET,
@@ -909,8 +494,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);
 	ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMON, feature_automonitor, NULL);
 	ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMIXMON, feature_automixmonitor, NULL);

Modified: trunk/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/bridging.h?view=diff&rev=395151&r1=395150&r2=395151
==============================================================================
--- trunk/include/asterisk/bridging.h (original)
+++ trunk/include/asterisk/bridging.h Tue Jul 23 10:28:11 2013
@@ -396,6 +396,8 @@
 struct ast_bridge {
 	/*! Bridge virtual method table. */
 	const struct ast_bridge_methods *v_table;
+	/*! "Personality" currently exhibited by bridge subclass */
+	void *personality;
 	/*! Immutable bridge UUID. */
 	char uniqueid[AST_UUID_STR_LEN];
 	/*! Bridge technology that is handling the bridge */
@@ -1785,6 +1787,16 @@
  */
 struct ast_channel *ast_bridge_peer(struct ast_bridge *bridge, struct ast_channel *chan);
 
+/*!
+ * \brief Remove marked bridge channel feature hooks.
+ * \since 12.0.0
+ *
+ * \param features Bridge features structure
+ * \param flags Determinator for whether hook is removed.
+ *
+ * \return Nothing
+ */
+void ast_bridge_features_remove(struct ast_bridge_features *features, enum ast_bridge_hook_remove_flags flags);
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif

Modified: trunk/include/asterisk/bridging_features.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/bridging_features.h?view=diff&rev=395151&r1=395150&r2=395151
==============================================================================
--- trunk/include/asterisk/bridging_features.h (original)
+++ trunk/include/asterisk/bridging_features.h Tue Jul 23 10:28:11 2013
@@ -183,6 +183,8 @@
 enum ast_bridge_hook_remove_flags {
 	/*! The hook is removed when the channel is pulled from the bridge. */
 	AST_BRIDGE_HOOK_REMOVE_ON_PULL = (1 << 0),
+	/*! The hook is removed when the bridge's personality changes. */
+	AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE = (1 << 1),
 };
 
 /* BUGBUG Need to be able to selectively remove DTMF, hangup, and interval hooks. */
@@ -265,6 +267,8 @@
 	char threeway[MAXIMUM_DTMF_FEATURE_STRING];
 	/*! DTMF string used to complete the transfer (If not empty.) */
 	char complete[MAXIMUM_DTMF_FEATURE_STRING];
+	/*! DTMF string used to swap bridged targets (If not empty.) */
+	char swap[MAXIMUM_DTMF_FEATURE_STRING];
 };
 
 enum ast_bridge_features_monitor {

Added: trunk/include/asterisk/bridging_internal.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/bridging_internal.h?view=auto&rev=395151
==============================================================================
--- trunk/include/asterisk/bridging_internal.h (added)
+++ trunk/include/asterisk/bridging_internal.h Tue Jul 23 10:28:11 2013
@@ -1,0 +1,88 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013 Digium, Inc.
+ *
+ * Mark Michelson <mmichelson at 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
+ * \brief Private Bridging API
+ *
+ * \author Mark Michelson <mmichelson at digium.com>
+ *
+ * See Also:
+ * \arg \ref AstCREDITS
+ */
+
+#ifndef _ASTERISK_PRIVATE_BRIDGING_H
+#define _ASTERISK_PRIVATE_BRIDGING_H
+
+struct ast_bridge;
+struct ast_bridge_channel;
+
+/*!
+ * \internal
+ * \brief Move a bridge channel from one bridge to another.
+ * \since 12.0.0
+ *
+ * \param dst_bridge Destination bridge of bridge channel move.
+ * \param bridge_channel Channel moving from one bridge to another.
+ * \param attempt_recovery TRUE if failure attempts to push channel back into original bridge.
+ * \param optimized Indicates whether the move is part of an unreal channel optimization.
+ *
+ * \note The dst_bridge and bridge_channel->bridge are assumed already locked.
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int bridge_move_do(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bridge_channel,
+		int attempt_recovery, unsigned int optimized);
+
+/*!
+ * \internal
+ * \brief Do the merge of two bridges.
+ * \since 12.0.0
+ *
+ * \param dst_bridge Destination bridge of merge.
+ * \param src_bridge Source bridge of merge.
+ * \param kick_me Array of channels to kick from the bridges.
+ * \param num_kick Number of channels in the kick_me array.
+ * \param optimized Indicates whether the merge is part of an unreal channel optimization.
+ *
+ * \return Nothing
+ *
+ * \note The two bridges are assumed already locked.
+ *
+ * This moves the channels in src_bridge into the bridge pointed
+ * to by dst_bridge.
+ */
+void bridge_merge_do(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge,
+		struct ast_bridge_channel **kick_me, unsigned int num_kick, unsigned int optimized);
+
+/*!
+ * \internal
+ * \brief Helper function to find a bridge channel given a channel.
+ *
+ * \param bridge What to search
+ * \param chan What to search for.
+ *
+ * \note On entry, bridge is already locked.
+ *
+ * \retval bridge_channel if channel is in the bridge.
+ * \retval NULL if not in bridge.
+ */
+struct ast_bridge_channel *find_bridge_channel(struct ast_bridge *bridge, struct ast_channel *chan);
+
+#endif /* _ASTERISK_PRIVATE_BRIDGING_H */

Propchange: trunk/include/asterisk/bridging_internal.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: trunk/include/asterisk/bridging_internal.h
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: trunk/include/asterisk/bridging_internal.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: trunk/include/asterisk/bridging_roles.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/bridging_roles.h?view=diff&rev=395151&r1=395150&r2=395151
==============================================================================
--- trunk/include/asterisk/bridging_roles.h (original)
+++ trunk/include/asterisk/bridging_roles.h Tue Jul 23 10:28:11 2013
@@ -64,6 +64,37 @@
 int ast_channel_set_bridge_role_option(struct ast_channel *channel, const char *role_name, const char *option, const char *value);
 
 /*!
+ * \brief Check if a role exists on a channel
+ *
+ * \param channel The channel to check
+ * \param role_name The name of the role to search for
+ *
+ * \retval 0 The requested role does not exist on the channel
+ * \retval 1 The requested role exists on the channel
+ *
+ * This is an alternative to \ref ast_bridge_channel_has_role that is useful if bridge
+ * roles have not yet been established on a channel's bridge_channel. A possible example of
+ * when this could be used is in a bridge v_table's push() callback.
+ */
+int ast_channel_has_role(struct ast_channel *channel, const char *role_name);
+
+/*!
+ * \brief Retrieve the value of a requested role option from a channel
+ *
+ * \param channel The channel to retrieve the requested option from
+ * \param role_name The role to which the option belongs
+ * \param option The name of the option to retrieve
+ *
+ * \retval NULL The option does not exist
+ * \retval non-NULL The value of the option
+ *
+ * This is an alternative to \ref ast_bridge_channel_get_role_option that is useful if bridge
+ * roles have not yet been esstablished on a channel's bridge_channel. A possible example of
+ * when this could be used is in a bridge v_table's push() callback.
+ */
+const char *ast_channel_get_role_option(struct ast_channel *channel, const char *role_name, const char *option);
+
+/*!
  * \brief Check to see if a bridge channel inherited a specific role from its channel
  *
  * \param bridge_channel The bridge channel being checked
@@ -72,7 +103,7 @@
  * \retval 0 The bridge channel does not have the requested role
  * \retval 1 The bridge channel does have the requested role
  *
- * \note Before a bridge_channel can effectively check roles against a bridge, ast_bridge_roles_bridge_channel_establish_roles
+ * \note Before a bridge_channel can effectively check roles against a bridge, ast_bridge_channel_establish_roles
  *       should be called on the bridge_channel so that roles and their respective role options can be copied from the channel
  *       datastore into the bridge_channel roles list. Otherwise this function will just return 0 because the list will be NULL.
  */
@@ -88,10 +119,10 @@
  * \retval NULL If either the role does not exist on the bridge_channel or the role does exist but the option has not been set
  * \retval The value of the option
  *
- * \note See ast_bridge_roles_channel_set_role_option note about the need to call ast_bridge_roles_bridge_channel_establish_roles.
+ * \note See ast_channel_set_role_option note about the need to call ast_bridge_channel_establish_roles.
  *
  * \note The returned character pointer is only valid as long as the bridge_channel is guaranteed to be alive and hasn't had
- *       ast_bridge_roles_bridge_channel_clear_roles called against it (as this will free all roles and role options in the bridge
+ *       ast_bridge_channel_clear_roles called against it (as this will free all roles and role options in the bridge
  *       channel). If you need this value after one of these destruction events occurs, you must make a local copy while it is
  *       still valid.
  */
@@ -119,12 +150,12 @@
  * \param bridge_channel the bridge channel that we are scrubbing
  *
  * \details
- * If roles are already established on a bridge channel, ast_bridge_roles_bridge_channel_establish_roles will fail unconditionally
+ * If roles are already established on a bridge channel, ast_bridge_channel_establish_roles will fail unconditionally
  * without changing any roles. In order to update a bridge channel's roles, they must first be cleared from the bridge channel using
  * this function.
  *
  * \note
- * ast_bridge_roles_bridge_channel_clear_roles also serves as the destructor for the role list of a bridge channel.
+ * ast_bridge_channel_clear_roles also serves as the destructor for the role list of a bridge channel.
  */
 void ast_bridge_channel_clear_roles(struct ast_bridge_channel *bridge_channel);
 

Modified: trunk/include/asterisk/features_config.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/features_config.h?view=diff&rev=395151&r1=395150&r2=395151
==============================================================================
--- trunk/include/asterisk/features_config.h (original)
+++ trunk/include/asterisk/features_config.h Tue Jul 23 10:28:11 2013
@@ -66,6 +66,8 @@
 		AST_STRING_FIELD(atxfercomplete);
 		/*! DTMF sequence used to turn an attempted atxfer into a three-way call */
 		AST_STRING_FIELD(atxferthreeway);
+		/*! DTMF sequence used to swap which party the transferer is talking to */
+		AST_STRING_FIELD(atxferswap);
 	);
 	/*! Milliseconds allowed between digit presses when dialing transfer destination */
 	unsigned int transferdigittimeout;

Modified: trunk/include/asterisk/stasis_bridging.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/stasis_bridging.h?view=diff&rev=395151&r1=395150&r2=395151
==============================================================================
--- trunk/include/asterisk/stasis_bridging.h (original)
+++ trunk/include/asterisk/stasis_bridging.h Tue Jul 23 10:28:11 2013
@@ -256,6 +256,8 @@
 	AST_ATTENDED_TRANSFER_DEST_APP,
 	/*! The transfer results in both bridges remaining with a local channel linking them */
 	AST_ATTENDED_TRANSFER_DEST_LINK,
+	/*! The transfer results in a threeway call between transferer, transferee, and transfer target */
+	AST_ATTENDED_TRANSFER_DEST_THREEWAY,
 };
 
 /*!
@@ -279,6 +281,8 @@
 		char app[AST_MAX_APP];
 		/*! Pair of local channels linking the bridges. Applicable for AST_ATTENDED_TRANSFER_DEST_LINK */
 		struct ast_channel_snapshot *links[2];
+		/*! Transferer channel and bridge that survived the transition to a threeway call. Applicable for AST_ATTENDED_TRANSFER_DEST_THREEWAY */
+		struct ast_bridge_channel_snapshot_pair threeway;
 	} dest;
 };
 
@@ -330,6 +334,25 @@
 
 /*!
  * \since 12
+ * \brief Publish an attended transfer that results in a threeway call.
+ *
+ * Publish an \ref ast_attended_transfer_message with the dest_type set to
+ * \c AST_ATTENDED_TRANSFER_DEST_THREEWAY. Like with \ref ast_bridge_publish_attended_transfer_bridge_merge,
+ * this results from merging two bridges together. The difference is that a
+ * transferer channel survives the bridge merge
+ *
+ * \param is_external Indicates if the transfer was initiated externally
+ * \param result The result of the transfer.
+ * \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
+ * \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
+ * \param final_pair The bridge that the parties end up in, and the transferer channel that is in this bridge.
+ */
+void ast_bridge_publish_attended_transfer_threeway(int is_external, enum ast_transfer_result result,
+		struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
+		struct ast_bridge_channel_pair *final_pair);
+
+/*!
+ * \since 12
  * \brief Publish an attended transfer that results in an application being run
  *
  * Publish an \ref ast_attended_transfer_message with the dest_type set to

Modified: trunk/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/bridging.c?view=diff&rev=395151&r1=395150&r2=395151
==============================================================================
--- trunk/main/bridging.c (original)
+++ trunk/main/bridging.c Tue Jul 23 10:28:11 2013
@@ -63,6 +63,7 @@
 #include "asterisk/core_local.h"
 #include "asterisk/core_unreal.h"
 #include "asterisk/features_config.h"
+#include "asterisk/bridging_internal.h"
 
 /*! All bridges container. */
 static struct ao2_container *bridges;
@@ -77,7 +78,6 @@
 
 static void cleanup_video_mode(struct ast_bridge *bridge);
 static int bridge_make_compatible(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
-static void bridge_features_remove(struct ast_bridge_features *features, enum ast_bridge_hook_remove_flags remove_flags);
 
 /*! Default DTMF keys for built in features */
 static char builtin_features_dtmf[AST_BRIDGE_BUILTIN_END][MAXIMUM_DTMF_FEATURE_STRING];
@@ -463,19 +463,7 @@
 	}
 }
 
-/*!
- * \internal
- * \brief Helper function to find a bridge channel given a channel.
- *
- * \param bridge What to search
- * \param chan What to search for.
- *
- * \note On entry, bridge is already locked.
- *
- * \retval bridge_channel if channel is in the bridge.
- * \retval NULL if not in bridge.
- */
-static struct ast_bridge_channel *find_bridge_channel(struct ast_bridge *bridge, struct ast_channel *chan)
+struct ast_bridge_channel *find_bridge_channel(struct ast_bridge *bridge, struct ast_channel *chan)
 {
 	struct ast_bridge_channel *bridge_channel;
 
@@ -757,7 +745,7 @@
 		|| ast_bridge_channel_establish_roles(bridge_channel)) {
 		ast_debug(1, "Bridge %s: pushing %p(%s) into bridge failed\n",
 			bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan));
-		bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
+		ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
 		return -1;
 	}
 	bridge_channel->in_bridge = 1;
@@ -1719,7 +1707,7 @@
  */
 static void bridge_base_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
 {
-	bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
+	ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
 }
 
 /*!
@@ -4164,24 +4152,7 @@
 	ao2_ref(old_bridge, -1);
 }
 
-/*!
- * \internal
- * \brief Do the merge of two bridges.
- * \since 12.0.0
- *
- * \param dst_bridge Destination bridge of merge.
- * \param src_bridge Source bridge of merge.
- * \param kick_me Array of channels to kick from the bridges.
- * \param num_kick Number of channels in the kick_me array.
- *
- * \return Nothing
- *
- * \note The two bridges are assumed already locked.
- *
- * This moves the channels in src_bridge into the bridge pointed
- * to by dst_bridge.
- */
-static void bridge_merge_do(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_bridge_channel **kick_me, unsigned int num_kick,
+void bridge_merge_do(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_bridge_channel **kick_me, unsigned int num_kick,
 	unsigned int optimized)
 {
 	struct ast_bridge_channel *bridge_channel;
@@ -4440,21 +4411,7 @@
 	return res;
 }
 
-/*!
- * \internal
- * \brief Move a bridge channel from one bridge to another.
- * \since 12.0.0
- *
- * \param dst_bridge Destination bridge of bridge channel move.
- * \param bridge_channel Channel moving from one bridge to another.
- * \param attempt_recovery TRUE if failure attempts to push channel back into original bridge.
- *
- * \note The dst_bridge and bridge_channel->bridge are assumed already locked.
- *
- * \retval 0 on success.
- * \retval -1 on failure.
- */
-static int bridge_move_do(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bridge_channel, int attempt_recovery,
+int bridge_move_do(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bridge_channel, int attempt_recovery,
 	unsigned int optimized)
 {
 	struct ast_bridge *orig_bridge;
@@ -5646,17 +5603,7 @@
 	ast_heap_unlock(hooks);
 }
 
-/*!
- * \internal
- * \brief Remove marked bridge channel feature hooks.
- * \since 12.0.0
- *
- * \param features Bridge features structure
- * \param remove_flags Determinator for whether hook is removed.
- *
- * \return Nothing
- */
-static void bridge_features_remove(struct ast_bridge_features *features, enum ast_bridge_hook_remove_flags remove_flags)
+void ast_bridge_features_remove(struct ast_bridge_features *features, enum ast_bridge_hook_remove_flags remove_flags)
 {
 	hooks_remove_container(features->dtmf_hooks, remove_flags);
 	hooks_remove_container(features->hangup_hooks, remove_flags);

Modified: trunk/main/bridging_basic.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/bridging_basic.c?view=diff&rev=395151&r1=395150&r2=395151
==============================================================================
--- trunk/main/bridging_basic.c (original)
+++ trunk/main/bridging_basic.c Tue Jul 23 10:28:11 2013
@@ -37,6 +37,43 @@
 #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"
+#include "asterisk/bridging_internal.h"
+#include "asterisk/dial.h"
+#include "asterisk/stasis_bridging.h"
+
+#define NORMAL_FLAGS	(AST_BRIDGE_FLAG_DISSOLVE_HANGUP | AST_BRIDGE_FLAG_DISSOLVE_EMPTY \
+			| AST_BRIDGE_FLAG_SMART)
+
+#define TRANSFER_FLAGS AST_BRIDGE_FLAG_SMART
+#define TRANSFERER_ROLE_NAME "transferer"
+
+struct attended_transfer_properties;
+
+enum bridge_basic_personality_type {
+	/*! Index for "normal" basic bridge personality */
+	BRIDGE_BASIC_PERSONALITY_NORMAL,
+	/*! Index for attended transfer basic bridge personality */
+	BRIDGE_BASIC_PERSONALITY_ATXFER,
+	/*! Indicates end of enum. Must always remain the last element */
+	BRIDGE_BASIC_PERSONALITY_END,
+};
+
+/*!
+ * \brief Change basic bridge personality
+ *
+ * Changing personalities allows for the bridge to remain in use but have
+ * properties such as its v_table and its flags change.
+ *
+ * \param bridge The bridge
+ * \param type The personality to change the bridge to
+ * \user_data Private data to attach to the personality.
+ */
+static void bridge_basic_change_personality(struct ast_bridge *bridge,
+		enum bridge_basic_personality_type type, void *user_data);
 
 /* ------------------------------------------------------------------- */
 
@@ -117,6 +154,37 @@
 }
 
 /*!
+ * \brief Details for specific basic bridge personalities
+ */
+struct personality_details {
+	/*! The v_table to use for this personality */
+	struct ast_bridge_methods *v_table;
+	/*! Flags to set on this type of bridge */
+	unsigned int bridge_flags;
+	/*! User data for this personality. If used, must be an ao2 object */
+	void *pvt;
+	/*! Callback to be called when changing to the personality */
+	void (*on_personality_change)(struct ast_bridge *bridge);
+};
+
+/*!
+ * \brief structure that organizes different personalities for basic bridges.
+ */
+struct bridge_basic_personality {
+	/*! The current bridge personality in use */
+	enum bridge_basic_personality_type current;
+	/*! Array of details for the types of bridge personalities supported */
+	struct personality_details details[BRIDGE_BASIC_PERSONALITY_END];
+};
+
+static int add_normal_hooks(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+	return ast_bridge_hangup_hook(bridge_channel->features, basic_hangup_hook,
+			NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL)
+		|| ast_bridge_channel_setup_features(bridge_channel);
+}
+
+/*!
  * \internal
  * \brief ast_bridge basic push method.
  * \since 12.0.0
@@ -130,31 +198,2576 @@
  * \retval 0 on success
  * \retval -1 on failure
  */
-static int bridge_basic_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
-{
-	if (ast_bridge_hangup_hook(bridge_channel->features, basic_hangup_hook, NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL)
-		|| ast_bridge_channel_setup_features(bridge_channel)) {
+static int bridge_personality_normal_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
+{
+	if (add_normal_hooks(self, bridge_channel)) {
 		return -1;
 	}
 
 	ast_bridge_update_accountcodes(self, bridge_channel, swap);
 	ast_bridge_update_linkedids(self, bridge_channel, swap);
+	return 0;
+}
+
+static int bridge_basic_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
+{
+	struct bridge_basic_personality *personality = self->personality;
+
+	ast_assert(personality != NULL);
+
+	if (personality->details[personality->current].v_table->push(self, bridge_channel, swap)) {
+		return -1;
+	}
 
 	return ast_bridge_base_v_table.push(self, bridge_channel, swap);
 }
 
+static void bridge_basic_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
+{
+	struct bridge_basic_personality *personality = self->personality;
+
+	ast_assert(personality != NULL);
+
+	if (personality->details[personality->current].v_table->pull) {
+		personality->details[personality->current].v_table->pull(self, bridge_channel);
+	}
+
+	ast_bridge_base_v_table.pull(self, bridge_channel);
+}
+

[... 2805 lines stripped ...]



More information about the svn-commits mailing list