[asterisk-commits] mmichelson: trunk r389848 - in /trunk: ./ apps/confbridge/ bridges/ channels/...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue May 28 09:45:35 CDT 2013


Author: mmichelson
Date: Tue May 28 09:45:31 2013
New Revision: 389848

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=389848
Log:
Adds support for a core attended transfer function plus adds some hiding of masquerades.

The attended transfer API call can complete the attended transfer in a number of ways
depending on the current bridged states of the channels involved.

The hiding of masquerades is done in some bridging-related functions, such as the manager
Bridge action and the Bridge dialplan application. In addition, call pickup was edited
to "move" a channel rather than masquerade it.

Review: https://origsvn.digium.com/svn/asterisk/r/2511

(closes issue ASTERISK-21334)
Reported by Matt Jordan

(closes issue Asterisk-21336)
Reported by Matt Jordan


Modified:
    trunk/CHANGES
    trunk/apps/confbridge/confbridge_manager.c
    trunk/bridges/bridge_builtin_features.c
    trunk/channels/chan_mgcp.c
    trunk/channels/chan_sip.c
    trunk/include/asterisk/bridging.h
    trunk/include/asterisk/channel.h
    trunk/main/bridging.c
    trunk/main/channel.c
    trunk/main/features.c
    trunk/main/pbx.c

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=389848&r1=389847&r2=389848
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Tue May 28 09:45:31 2013
@@ -91,6 +91,12 @@
 
  * The JabberEvent event has been removed. It is not AMI's purpose to be a
    carrier for another protocol.
+
+ * The Bridge Manager action's Playtone header now accepts more fine-grained
+   options. "Channel1" and "Channel2" may be specified in order to play a tone
+   to the specific channel. "Both" may be specified to play a tone to both
+   channels. The old "yes" option is still accepted as a way of playing the
+   tone to Channel2 only.
 
  * The AMI 'Status' response event to the AMI Status action replaces the
    BridgedChannel and BridgedUniqueid headers with the BridgeID header to

Modified: trunk/apps/confbridge/confbridge_manager.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/confbridge/confbridge_manager.c?view=diff&rev=389848&r1=389847&r2=389848
==============================================================================
--- trunk/apps/confbridge/confbridge_manager.c (original)
+++ trunk/apps/confbridge/confbridge_manager.c Tue May 28 09:45:31 2013
@@ -161,6 +161,7 @@
 			</see-also>
 		</managerEventInstance>
 	</managerEvent>
+
 	<managerEvent language="en_US" name="ConfbridgeTalking">
 		<managerEventInstance class="EVENT_FLAG_CALL">
 			<synopsis>Raised when a confbridge participant unmutes.</synopsis>

Modified: trunk/bridges/bridge_builtin_features.c
URL: http://svnview.digium.com/svn/asterisk/trunk/bridges/bridge_builtin_features.c?view=diff&rev=389848&r1=389847&r2=389848
==============================================================================
--- trunk/bridges/bridge_builtin_features.c (original)
+++ trunk/bridges/bridge_builtin_features.c Tue May 28 09:45:31 2013
@@ -95,6 +95,16 @@
 	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)
 {
@@ -113,12 +123,7 @@
 	}
 
 	/* Before we actually dial out let's inherit appropriate information. */
-	ast_channel_lock_both(caller, chan);
-	ast_connected_line_copy_from_caller(ast_channel_connected(chan), ast_channel_caller(caller));
-	ast_channel_inherit_variables(caller, chan);
-	ast_channel_datastore_inherit(caller, chan);
-	ast_channel_unlock(chan);
-	ast_channel_unlock(caller);
+	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)) {
@@ -159,19 +164,30 @@
 	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_channel *chan = NULL;
 	struct ast_bridge_features_blind_transfer *blind_transfer = hook_pvt;
 	const char *context;
-	struct ast_exten *park_exten;
+	char *goto_on_blindxfr;
 
 /* BUGBUG the peer needs to be put on hold for the transfer. */
 	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 */
@@ -179,30 +195,16 @@
 		return 0;
 	}
 
-	/* Parking blind transfer override - phase this out for something more general purpose in the future. */
-	park_exten = ast_get_parking_exten(exten, bridge_channel->chan, context);
-	if (park_exten) {
-		/* We are transfering the transferee to a parking lot. */
-		if (ast_park_blind_xfer(bridge, bridge_channel, park_exten)) {
-			ast_log(LOG_ERROR, "%s attempted to transfer to park application and failed.\n", ast_channel_name(bridge_channel->chan));
-		};
-		return 0;
-	}
-
-/* BUGBUG just need to ast_async_goto the peer so this bridge will go away and not accumulate local channels and bridges if the destination is to an application. */
-/* ast_async_goto actually is a blind transfer. */
-/* BUGBUG Use the bridge count to determine if can do DTMF transfer features.  If count is not 2 then don't allow it. */
-
-	/* Get a channel that is the destination we wish to call */
-	chan = dial_transfer(bridge_channel->chan, exten, context);
-	if (!chan) {
-		return 0;
-	}
-
-	/* Impart the new channel onto the bridge, and have it take our place. */
-	if (ast_bridge_impart(bridge_channel->bridge, chan, bridge_channel->chan, NULL, 1)) {
-		ast_hangup(chan);
-		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(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;

Modified: trunk/channels/chan_mgcp.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_mgcp.c?view=diff&rev=389848&r1=389847&r2=389848
==============================================================================
--- trunk/channels/chan_mgcp.c (original)
+++ trunk/channels/chan_mgcp.c Tue May 28 09:45:31 2013
@@ -3236,7 +3236,7 @@
 
 	ast_mutex_unlock(&p->sub->next->lock);
 	ast_mutex_unlock(&p->sub->lock);
-	res = ast_bridge_transfer_attended(sub->owner, sub->next->owner, NULL);
+	res = ast_bridge_transfer_attended(sub->owner, sub->next->owner);
 
 	/* 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

Modified: trunk/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_sip.c?view=diff&rev=389848&r1=389847&r2=389848
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Tue May 28 09:45:31 2013
@@ -26234,9 +26234,11 @@
  * we may send out.
  *
  * \param chan The new outbound channel
- * \user_data A blind_transfer_cb_data struct
+ * \param user_data A blind_transfer_cb_data struct
+ * \param transfer_type Unused
  */
-static void blind_transfer_cb(struct ast_channel *chan, void *user_data)
+static void blind_transfer_cb(struct ast_channel *chan, void *user_data,
+		enum ast_transfer_type transfer_type)
 {
 	struct blind_transfer_cb_data *cb_data = user_data;
 

Modified: trunk/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/bridging.h?view=diff&rev=389848&r1=389847&r2=389848
==============================================================================
--- trunk/include/asterisk/bridging.h (original)
+++ trunk/include/asterisk/bridging.h Tue May 28 09:45:31 2013
@@ -225,6 +225,8 @@
 	AST_BRIDGE_ACTION_RUN_APP,
 	/*! Bridge channel is to execute a blind transfer. */
 	AST_BRIDGE_ACTION_BLIND_TRANSFER,
+	/*! Bridge channel is to execute an attended transfer */
+	AST_BRIDGE_ACTION_ATTENDED_TRANSFER,
 
 	/*
 	 * Bridge actions put after this comment must never be put onto
@@ -879,6 +881,45 @@
 int ast_bridge_unreal_optimized_out(struct ast_channel *chan, struct ast_channel *peer);
 
 /*!
+ * \brief Tells, if optimization is allowed, how the optimization would be performed
+ */
+enum ast_bridge_optimization {
+	/*! Optimization would swap peer into the chan_bridge */
+	AST_BRIDGE_OPTIMIZE_SWAP_TO_CHAN_BRIDGE,
+	/*! Optimization would swap chan into the peer_bridge */
+	AST_BRIDGE_OPTIMIZE_SWAP_TO_PEER_BRIDGE,
+	/*! Optimization would merge peer_bridge into chan_bridge */
+	AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE,
+	/*! Optimization would merge chan_bridge into peer_bridge */
+	AST_BRIDGE_OPTIMIZE_MERGE_TO_PEER_BRIDGE,
+	/*! Optimization is not permitted on one or both bridges */
+	AST_BRIDGE_OPTIMIZE_PROHIBITED,
+};
+
+/*!
+ * \brief Determine if bridges allow for optimization to occur betweem them
+ * \since 12.0.0
+ *
+ * \param chan_bridge First bridge being tested
+ * \param peer_bridge Second bridge being tested
+ *
+ * This determines if two bridges allow for unreal channel optimization
+ * to occur between them. The function does not require for unreal channels
+ * to already be in the bridges when called.
+ *
+ * \note It is assumed that both bridges are locked prior to calling this function
+ *
+ * \note A return other than AST_BRIDGE_OPTIMIZE_PROHIBITED does not guarantee
+ * that an optimization attempt will succeed. However, a return of
+ * AST_BRIDGE_OPTIMIZE_PROHIBITED guarantees that an optimization attempt will
+ * never succeed.
+ *
+ * \returns Optimization allowability for the bridges
+ */
+enum ast_bridge_optimization ast_bridges_allow_optimization(struct ast_bridge *chan_bridge,
+		struct ast_bridge *peer_bridge);
+
+/*!
  * \brief Try locking the bridge_channel.
  *
  * \param bridge_channel What to try locking
@@ -1288,7 +1329,26 @@
 	AST_BRIDGE_TRANSFER_FAIL,
 };
 
-typedef void (*transfer_channel_cb)(struct ast_channel *chan, void *user_data);
+enum ast_transfer_type {
+	/*! Transfer of a single party */
+	AST_BRIDGE_TRANSFER_SINGLE_PARTY,
+	/*! Transfer of multiple parties */
+	AST_BRIDGE_TRANSFER_MULTI_PARTY,
+};
+
+/*!
+ * \brief Callback function type called during blind transfers
+ *
+ * A caller of ast_bridge_transfer_blind() may wish to set data on
+ * the channel that ends up running dialplan. For instance, it may
+ * be useful to set channel variables on the channel.
+ *
+ * \param chan The involved channel
+ * \param user_data User-provided data needed in the callback
+ * \param transfer_type The type of transfer being completed
+ */
+typedef void (*transfer_channel_cb)(struct ast_channel *chan, void *user_data,
+		enum ast_transfer_type transfer_type);
 
 /*!
  * \brief Blind transfer target to the extension and context provided
@@ -1326,20 +1386,15 @@
  * the transfer). The second is the channel that is bridged to the transfer
  * target (or if unbridged, the 'second' call of the transfer).
  *
- * Like with a blind transfer, a frame hook can be provided to monitor the
- * resulting call after the transfer completes. If the transfer fails, the
- * hook will not be attached to any call.
- *
  * \note Absolutely _NO_ channel locks should be held before
  * calling this function.
  *
  * \param to_transferee Transferer channel on initial call (presumably bridged to transferee)
  * \param to_transfer_target Transferer channel on consultation call (presumably bridged to transfer target)
- * \param hook A frame hook to attach to the resultant call
  * \return The success or failure of the attended transfer
  */
 enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_transferee,
-		struct ast_channel *to_transfer_target, struct ast_framehook *hook);
+		struct ast_channel *to_transfer_target);
 /*!
  * \brief Set channel to goto specific location after the bridge.
  * \since 12.0.0
@@ -1512,6 +1567,16 @@
 int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb callback, ast_after_bridge_cb_failed failed, void *data);
 
 /*!
+ * \brief Get a string representation of an after bridge callback reason
+ * \since 12.0.0
+ *
+ * \param reason The reason to interpret to a string
+ * \retval NULL Unrecognized reason
+ * \retval non-NULL String representation of reason
+ */
+const char *ast_after_bridge_cb_reason_string(enum ast_after_bridge_cb_reason reason);
+
+/*!
  * \brief Get a container of all channels in the bridge
  * \since 12.0.0
  *

Modified: trunk/include/asterisk/channel.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/channel.h?view=diff&rev=389848&r1=389847&r2=389848
==============================================================================
--- trunk/include/asterisk/channel.h (original)
+++ trunk/include/asterisk/channel.h Tue May 28 09:45:31 2013
@@ -4229,4 +4229,55 @@
  */
 struct ast_bridge_channel *ast_channel_get_bridge_channel(struct ast_channel *chan);
 
+/*!
+ * \since 12
+ * \brief Gain control of a channel in the system
+ *
+ * The intention of this function is to take a channel that currently
+ * is running in one thread and gain control of it in the current thread.
+ * This can be used to redirect a channel to a different place in the dialplan,
+ * for instance.
+ *
+ * \note This function is NOT intended to be used on bridged channels. If you
+ * need to control a bridged channel, you can set a callback to be called
+ * once the channel exits the bridge, and run your controlling logic in that
+ * callback
+ *
+ * XXX Put name of callback-setting function in above paragraph once it is written
+ *
+ * \note When this function returns successfully, the yankee channel is in a state where
+ * it cannot be used any further. Always use the returned channel instead.
+ *
+ * \note absolutely _NO_ channel locks should be held before calling this function.
+ *
+ * \param yankee The channel to gain control of
+ * \retval NULL Could not gain control of the channel
+ * \retval non-NULL The channel
+ */
+struct ast_channel *ast_channel_yank(struct ast_channel *yankee);
+
+/*!
+ * \since 12
+ * \brief Move a channel from its current location to a new location
+ *
+ * The intention of this function is to have the destination channel
+ * take on the identity of the source channel.
+ *
+ * \note This function is NOT intended to be used on bridged channels. If you
+ * wish to move an unbridged channel into the place of a bridged channel, then
+ * use ast_bridge_join() or ast_bridge_impart(). If you wish to move a bridged
+ * channel into the place of another bridged channel, then use ast_bridge_move().
+ *
+ * \note When this function returns succesfully, the source channel is in a
+ * state where its continued use is unreliable.
+ *
+ * \note absolutely _NO_ channel locks should be held before calling this function.
+ *
+ * \param dest The place to move the source channel
+ * \param source The channel to move
+ * \retval 0 Success
+ * \retval non-zero Failure
+ */
+int ast_channel_move(struct ast_channel *dest, struct ast_channel *source);
+
 #endif /* _ASTERISK_CHANNEL_H */

Modified: trunk/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/bridging.c?view=diff&rev=389848&r1=389847&r2=389848
==============================================================================
--- trunk/main/bridging.c (original)
+++ trunk/main/bridging.c Tue May 28 09:45:31 2013
@@ -59,6 +59,7 @@
 #include "asterisk/features.h"
 #include "asterisk/cli.h"
 #include "asterisk/parking.h"
+#include "asterisk/core_local.h"
 
 /*! All bridges container. */
 static struct ao2_container *bridges;
@@ -2000,6 +2001,48 @@
 		struct blind_transfer_data *blind_data)
 {
 	ast_async_goto(bridge_channel->chan, blind_data->context, blind_data->exten, 1);
+	bridge_handle_hangup(bridge_channel);
+}
+
+static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *data)
+{
+	RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup);
+	ast_channel_move(chan_target, chan_bridged);
+}
+
+static void after_bridge_move_channel_fail(enum ast_after_bridge_cb_reason reason, void *data)
+{
+	RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup);
+
+	ast_log(LOG_WARNING, "Unable to complete transfer: %s\n",
+			ast_after_bridge_cb_reason_string(reason));
+}
+
+static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_channel,
+		const char *target_chan_name)
+{
+	RAII_VAR(struct ast_channel *, chan_target, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_channel *, chan_bridged, NULL, ao2_cleanup);
+
+	chan_target = ast_channel_get_by_name(target_chan_name);
+	if (!chan_target) {
+		/* Dang, it disappeared somehow */
+		return;
+	}
+
+	{
+		SCOPED_CHANNELLOCK(lock, bridge_channel);
+		chan_bridged = bridge_channel->chan;
+		if (!chan_bridged) {
+			return;
+		}
+		ao2_ref(chan_bridged, +1);
+	}
+
+	if (ast_after_bridge_callback_set(chan_bridged, after_bridge_move_channel,
+			after_bridge_move_channel_fail, ast_channel_ref(chan_target))) {
+		return;
+	}
 	bridge_handle_hangup(bridge_channel);
 }
 
@@ -2066,6 +2109,9 @@
 	case AST_BRIDGE_ACTION_BLIND_TRANSFER:
 		bridge_channel_blind_transfer(bridge_channel, action->data.ptr);
 		break;
+	case AST_BRIDGE_ACTION_ATTENDED_TRANSFER:
+		bridge_channel_attended_transfer(bridge_channel, action->data.ptr);
+		break;
 	default:
 		break;
 	}
@@ -2714,6 +2760,23 @@
 	return 0;
 }
 
+const char *reason_strings[] = {
+	[AST_AFTER_BRIDGE_CB_REASON_DESTROY] = "Bridge Destroyed",
+	[AST_AFTER_BRIDGE_CB_REASON_REPLACED] = "Channel replaced",
+	[AST_AFTER_BRIDGE_CB_REASON_MASQUERADE] = "Channel masqueraded",
+	[AST_AFTER_BRIDGE_CB_REASON_DEPART] = "Channel departed",
+	[AST_AFTER_BRIDGE_CB_REASON_REMOVED] = "Channel removed",
+};
+
+const char *ast_after_bridge_cb_reason_string(enum ast_after_bridge_cb_reason reason)
+{
+	if (reason < AST_AFTER_BRIDGE_CB_REASON_DESTROY || reason > AST_AFTER_BRIDGE_CB_REASON_REMOVED) {
+		return "Unknown";
+	}
+
+	return reason_strings[reason];
+}
+
 struct after_bridge_goto_ds {
 	/*! Goto string that can be parsed by ast_parseable_goto(). */
 	const char *parseable_goto;
@@ -3742,6 +3805,19 @@
 	return other;
 }
 
+static int bridge_allows_optimization(struct ast_bridge *bridge)
+{
+	return !(bridge->inhibit_merge
+		|| bridge->dissolved
+		|| ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY));
+}
+
+static int bridge_channel_allows_optimization(struct ast_bridge_channel *bridge_channel)
+{
+	return bridge_channel->in_bridge
+		&& AST_LIST_EMPTY(&bridge_channel->wr_queue);
+}
+
 /*!
  * \internal
  * \brief Lock the unreal channel stack for chan and prequalify it.
@@ -3773,11 +3849,8 @@
 		ast_bridge_channel_unlock(bridge_channel);
 		return NULL;
 	}
-	if (bridge->inhibit_merge
-		|| bridge->dissolved
-		|| ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)
-		|| !bridge_channel->in_bridge
-		|| !AST_LIST_EMPTY(&bridge_channel->wr_queue)) {
+	if (!bridge_channel_allows_optimization(bridge_channel) ||
+			!bridge_allows_optimization(bridge)) {
 		ast_bridge_unlock(bridge);
 		ast_bridge_channel_unlock(bridge_channel);
 		return NULL;
@@ -3820,11 +3893,8 @@
 		ast_channel_unlock(peer);
 		return NULL;
 	}
-	if (bridge->inhibit_merge
-		|| bridge->dissolved
-		|| ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)
-		|| !bridge_channel->in_bridge
-		|| !AST_LIST_EMPTY(&bridge_channel->wr_queue)) {
+	if (!bridge_allows_optimization(bridge) ||
+			!bridge_channel_allows_optimization(bridge_channel)) {
 		ast_bridge_unlock(bridge);
 		ast_bridge_channel_unlock(bridge_channel);
 		ast_channel_unlock(peer);
@@ -3835,33 +3905,37 @@
 
 /*!
  * \internal
- * \brief Check and attempt to swap optimize out the unreal channels.
- * \since 12.0.0
- *
- * \param chan_bridge
- * \param chan_bridge_channel
- * \param peer_bridge
- * \param peer_bridge_channel
- *
- * \retval 1 if unreal channels failed to optimize out.
- * \retval 0 if unreal channels were not optimized out.
- * \retval -1 if unreal channels were optimized out.
- */
-static int check_swap_optimize_out(struct ast_bridge *chan_bridge,
-	struct ast_bridge_channel *chan_bridge_channel, struct ast_bridge *peer_bridge,
-	struct ast_bridge_channel *peer_bridge_channel)
-{
-	struct ast_bridge *dst_bridge = NULL;
-	struct ast_bridge_channel *dst_bridge_channel = NULL;
-	struct ast_bridge_channel *src_bridge_channel = NULL;
+ * \brief Indicates allowability of a swap optimization
+ */
+enum bridge_allow_swap {
+	/*! Bridges cannot allow for a swap optimization to occur */
+	SWAP_PROHIBITED,
+	/*! Bridge swap optimization can occur into the chan_bridge */
+	SWAP_TO_CHAN_BRIDGE,
+	/*! Bridge swap optimization can occur into the peer_bridge */
+	SWAP_TO_PEER_BRIDGE,
+};
+
+/*!
+ * \internal
+ * \brief Determine if two bridges allow for swap optimization to occur
+ *
+ * \param chan_bridge First bridge being tested
+ * \param peer_bridge Second bridge being tested
+ * \return Allowability of swap optimization
+ */
+static enum bridge_allow_swap bridges_allow_swap_optimization(struct ast_bridge *chan_bridge,
+		struct ast_bridge *peer_bridge)
+{
+	int chan_priority;
 	int peer_priority;
-	int chan_priority;
-	int res = 0;
 
 	if (!ast_test_flag(&chan_bridge->feature_flags,
-			AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM)
+			AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM |
+			AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY)
 		&& !ast_test_flag(&peer_bridge->feature_flags,
-			AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM)) {
+			AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM |
+			AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY)) {
 		/*
 		 * Can swap either way.  Swap to the higher priority merge
 		 * bridge.
@@ -3870,53 +3944,29 @@
 		peer_priority = peer_bridge->v_table->get_merge_priority(peer_bridge);
 		if (chan_bridge->num_channels == 2
 			&& chan_priority <= peer_priority) {
-			dst_bridge = peer_bridge;
-			dst_bridge_channel = peer_bridge_channel;
-			src_bridge_channel = chan_bridge_channel;
+			return SWAP_TO_PEER_BRIDGE;
 		} else if (peer_bridge->num_channels == 2
 			&& peer_priority <= chan_priority) {
-			dst_bridge = chan_bridge;
-			dst_bridge_channel = chan_bridge_channel;
-			src_bridge_channel = peer_bridge_channel;
+			return SWAP_TO_CHAN_BRIDGE;
 		}
 	} else if (chan_bridge->num_channels == 2
-		&& !ast_test_flag(&chan_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM)
+		&& !ast_test_flag(&chan_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY)
 		&& !ast_test_flag(&peer_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_TO)) {
 		/* Can swap optimize only one way. */
-		dst_bridge = peer_bridge;
-		dst_bridge_channel = peer_bridge_channel;
-		src_bridge_channel = chan_bridge_channel;
+		return SWAP_TO_PEER_BRIDGE;
 	} else if (peer_bridge->num_channels == 2
-		&& !ast_test_flag(&peer_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM)
+		&& !ast_test_flag(&peer_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY)
 		&& !ast_test_flag(&chan_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_TO)) {
 		/* Can swap optimize only one way. */
-		dst_bridge = chan_bridge;
-		dst_bridge_channel = chan_bridge_channel;
-		src_bridge_channel = peer_bridge_channel;
-	}
-	if (dst_bridge) {
-		struct ast_bridge_channel *other;
-
-		res = 1;
-		other = ast_bridge_channel_peer(src_bridge_channel);
-		if (other && other->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
-			ast_debug(1, "Move-swap optimizing %s <-- %s.\n",
-				ast_channel_name(dst_bridge_channel->chan),
-				ast_channel_name(other->chan));
-
-			other->swap = dst_bridge_channel->chan;
-			if (!bridge_move_do(dst_bridge, other, 1)) {
-				ast_bridge_change_state(src_bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
-				res = -1;
-			}
-		}
-	}
-	return res;
-}
-
-/*!
- * \internal
- * \brief Check and attempt to merge optimize out the unreal channels.
+		return SWAP_TO_CHAN_BRIDGE;
+	}
+
+	return SWAP_PROHIBITED;
+}
+
+/*!
+ * \internal
+ * \brief Check and attempt to swap optimize out the unreal channels.
  * \since 12.0.0
  *
  * \param chan_bridge
@@ -3924,6 +3974,108 @@
  * \param peer_bridge
  * \param peer_bridge_channel
  *
+ * \retval 1 if unreal channels failed to optimize out.
+ * \retval 0 if unreal channels were not optimized out.
+ * \retval -1 if unreal channels were optimized out.
+ */
+static int check_swap_optimize_out(struct ast_bridge *chan_bridge,
+	struct ast_bridge_channel *chan_bridge_channel, struct ast_bridge *peer_bridge,
+	struct ast_bridge_channel *peer_bridge_channel)
+{
+	struct ast_bridge *dst_bridge;
+	struct ast_bridge_channel *dst_bridge_channel;
+	struct ast_bridge_channel *src_bridge_channel;
+	struct ast_bridge_channel *other;
+	int res = 1;
+
+	switch (bridges_allow_swap_optimization(chan_bridge, peer_bridge)) {
+	case SWAP_TO_CHAN_BRIDGE:
+		dst_bridge = chan_bridge;
+		dst_bridge_channel = chan_bridge_channel;
+		src_bridge_channel = peer_bridge_channel;
+		break;
+	case SWAP_TO_PEER_BRIDGE:
+		dst_bridge = peer_bridge;
+		dst_bridge_channel = peer_bridge_channel;
+		src_bridge_channel = chan_bridge_channel;
+		break;
+	case SWAP_PROHIBITED:
+	default:
+		return 0;
+	}
+
+	other = ast_bridge_channel_peer(src_bridge_channel);
+	if (other && other->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+		ast_debug(1, "Move-swap optimizing %s <-- %s.\n",
+			ast_channel_name(dst_bridge_channel->chan),
+			ast_channel_name(other->chan));
+
+		other->swap = dst_bridge_channel->chan;
+		if (!bridge_move_do(dst_bridge, other, 1)) {
+			ast_bridge_change_state(src_bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+			res = -1;
+		}
+	}
+	return res;
+}
+
+/*!
+ * \internal
+ * \brief Indicates allowability of a merge optimization
+ */
+enum bridge_allow_merge {
+	/*! Bridge properties prohibit merge optimization */
+	MERGE_PROHIBITED,
+	/*! Merge optimization cannot occur because the source bridge has too few channels */
+	MERGE_NOT_ENOUGH_CHANNELS,
+	/*! Merge optimization cannot occur because multimix capability could not be requested */
+	MERGE_NO_MULTIMIX,
+	/*! Merge optimization allowed between bridges */
+	MERGE_ALLOWED,
+};
+
+/*!
+ * \internal
+ * \brief Determines allowability of a merge optimization
+ *
+ * \note The merge output parameter is undefined if MERGE_PROHIBITED is returned. For success
+ * and other failure returns, a merge direction was determined, and the parameter is safe to
+ * access.
+ *
+ * \param chan_bridge First bridge being tested
+ * \param peer_bridge Second bridge being tested
+ * \param num_kick_channels The number of channels to remove from the bridges during merging
+ * \param[out] merge Indicates the recommended direction for the bridge merge
+ */
+static enum bridge_allow_merge bridges_allow_merge_optimization(struct ast_bridge *chan_bridge,
+		struct ast_bridge *peer_bridge, int num_kick_channels, struct merge_direction *merge)
+{
+	*merge = bridge_merge_determine_direction(chan_bridge, peer_bridge);
+	if (!merge->dest) {
+		return MERGE_PROHIBITED;
+	}
+	if (merge->src->num_channels < 2) {
+		return MERGE_NOT_ENOUGH_CHANNELS;
+	} else if ((2 + num_kick_channels) < merge->dest->num_channels + merge->src->num_channels
+		&& !(merge->dest->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX)
+		&& (!ast_test_flag(&merge->dest->feature_flags, AST_BRIDGE_FLAG_SMART)
+			|| !(merge->dest->allowed_capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX))) {
+		return MERGE_NO_MULTIMIX;
+	}
+
+	return MERGE_ALLOWED;
+}
+
+/*!
+ * \internal
+ * \brief Check and attempt to merge optimize out the unreal channels.
+ * \since 12.0.0
+ *
+ * \param chan_bridge
+ * \param chan_bridge_channel
+ * \param peer_bridge
+ * \param peer_bridge_channel
+ *
  * \retval 0 if unreal channels were not optimized out.
  * \retval -1 if unreal channels were optimized out.
  */
@@ -3932,39 +4084,36 @@
 	struct ast_bridge_channel *peer_bridge_channel)
 {
 	struct merge_direction merge;
-	int res = 0;
-
-	merge = bridge_merge_determine_direction(chan_bridge, peer_bridge);
-	if (!merge.dest) {
-		return res;
-	}
-	if (merge.src->num_channels < 2) {
+	struct ast_bridge_channel *kick_me[] = {
+		chan_bridge_channel,
+		peer_bridge_channel,
+	};
+
+	switch (bridges_allow_merge_optimization(chan_bridge, peer_bridge, ARRAY_LEN(kick_me), &merge)) {
+	case MERGE_ALLOWED:
+		break;
+	case MERGE_PROHIBITED:
+		return 0;
+	case MERGE_NOT_ENOUGH_CHANNELS:
 		ast_debug(4, "Can't optimize %s -- %s out, not enough channels in bridge %s.\n",
 			ast_channel_name(chan_bridge_channel->chan),
 			ast_channel_name(peer_bridge_channel->chan),
 			merge.src->uniqueid);
-	} else if ((2 + 2) < merge.dest->num_channels + merge.src->num_channels
-		&& !(merge.dest->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX)
-		&& (!ast_test_flag(&merge.dest->feature_flags, AST_BRIDGE_FLAG_SMART)
-			|| !(merge.dest->allowed_capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX))) {
+		return 0;
+	case MERGE_NO_MULTIMIX:
 		ast_debug(4, "Can't optimize %s -- %s out, multimix is needed and it cannot be acquired.\n",
 			ast_channel_name(chan_bridge_channel->chan),
 			ast_channel_name(peer_bridge_channel->chan));
-	} else {
-		struct ast_bridge_channel *kick_me[] = {
-			chan_bridge_channel,
-			peer_bridge_channel,
-			};
-
-		ast_debug(1, "Merge optimizing %s -- %s out.\n",
-			ast_channel_name(chan_bridge_channel->chan),
-			ast_channel_name(peer_bridge_channel->chan));
-
-		bridge_merge_do(merge.dest, merge.src, kick_me, ARRAY_LEN(kick_me));
-		res = -1;
-	}
-
-	return res;
+		return 0;
+	}
+
+	ast_debug(1, "Merge optimizing %s -- %s out.\n",
+		ast_channel_name(chan_bridge_channel->chan),
+		ast_channel_name(peer_bridge_channel->chan));
+
+	bridge_merge_do(merge.dest, merge.src, kick_me, ARRAY_LEN(kick_me));
+
+	return -1;
 }
 
 int ast_bridge_unreal_optimized_out(struct ast_channel *chan, struct ast_channel *peer)
@@ -4005,6 +4154,37 @@
 	ast_bridge_channel_unlock(chan_bridge_channel);
 
 	return res;
+}
+
+enum ast_bridge_optimization ast_bridges_allow_optimization(struct ast_bridge *chan_bridge,
+		struct ast_bridge *peer_bridge)
+{
+	struct merge_direction merge;
+
+	if (!bridge_allows_optimization(chan_bridge) || !bridge_allows_optimization(peer_bridge)) {
+		return AST_BRIDGE_OPTIMIZE_PROHIBITED;
+	}
+
+	switch (bridges_allow_swap_optimization(chan_bridge, peer_bridge)) {
+	case SWAP_TO_CHAN_BRIDGE:
+		return AST_BRIDGE_OPTIMIZE_SWAP_TO_CHAN_BRIDGE;
+	case SWAP_TO_PEER_BRIDGE:
+		return AST_BRIDGE_OPTIMIZE_SWAP_TO_PEER_BRIDGE;
+	case SWAP_PROHIBITED:
+	default:
+		break;
+	}
+
+	/* Two channels will be kicked from the bridges, the unreal;1 and unreal;2 channels */
+	if (bridges_allow_merge_optimization(chan_bridge, peer_bridge, 2, &merge) != MERGE_ALLOWED) {
+		return AST_BRIDGE_OPTIMIZE_PROHIBITED;
+	}
+
+	if (merge.dest == chan_bridge) {
+		return AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE;
+	} else {
+		return AST_BRIDGE_OPTIMIZE_MERGE_TO_PEER_BRIDGE;
+	}
 }
 
 /*!
@@ -4999,7 +5179,7 @@
 	}
 
 	if (new_channel_cb) {
-		new_channel_cb(local, user_data);
+		new_channel_cb(local, user_data, AST_BRIDGE_TRANSFER_MULTI_PARTY);
 	}
 
 	if (ast_call(local, chan_name, 0)) {
@@ -5010,6 +5190,67 @@
 		ast_hangup(local);
 		return AST_BRIDGE_TRANSFER_FAIL;
 	}
+	return AST_BRIDGE_TRANSFER_SUCCESS;
+}
+
+/*!
+ * \brief Perform an attended transfer of a bridge
+ *
+ * This performs an attended transfer of an entire bridge to a target.
+ * The target varies, depending on what bridges exist during the transfer
+ * attempt.
+ *
+ * If two bridges exist, then a local channel is created to link the two
+ * bridges together. Local channel optimization may result in the bridges
+ * merging.
+ *
+ * If only one bridge exists, then a local channel is created with one end
+ * placed into the existing bridge and the other end masquerading into
+ * the unbridged channel.
+ *
+ * \param chan1 Transferer channel. Guaranteed to be bridged.
+ * \param chan2 Other transferer channel. May or may not be bridged.
+ * \param bridge1 Bridge that chan1 is in. Guaranteed to be non-NULL.
+ * \param bridge2 Bridge that chan2 is in. If NULL, then chan2 is not bridged.
+ * \retval AST_BRIDGE_TRANSFER_FAIL Internal error occurred
+ * \retval AST_BRIDGE_TRANSFER_SUCCESS Succesfully transferred the bridge
+ */
+static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *chan1,
+		struct ast_channel *chan2, struct ast_bridge *bridge1, struct ast_bridge *bridge2)
+{
+	static const char *dest = "_attended at transfer/m";
+	struct ast_channel *local_chan;
+	int cause;
+	int res;
+
+	local_chan = ast_request("Local", ast_channel_nativeformats(chan1), chan1,
+			dest, &cause);
+
+	if (!local_chan) {
+		return AST_BRIDGE_TRANSFER_FAIL;
+	}
+
+	if (bridge2) {
+		res = ast_local_setup_bridge(local_chan, bridge2, chan2, NULL);
+	} else {
+		res = ast_local_setup_masquerade(local_chan, chan2);
+	}
+
+	if (res) {
+		ast_hangup(local_chan);
+		return AST_BRIDGE_TRANSFER_FAIL;
+	}
+
+	if (ast_call(local_chan, dest, 0)) {
+		ast_hangup(local_chan);
+		return AST_BRIDGE_TRANSFER_FAIL;
+	}
+
+	if (ast_bridge_impart(bridge1, local_chan, chan1, NULL, 1)) {
+		ast_hangup(local_chan);
+		return AST_BRIDGE_TRANSFER_FAIL;
+	}
+
 	return AST_BRIDGE_TRANSFER_SUCCESS;
 }
 
@@ -5076,7 +5317,7 @@
 	}
 
 	if (new_channel_cb) {
-		new_channel_cb(transferee, user_data);
+		new_channel_cb(transferee, user_data, AST_BRIDGE_TRANSFER_SINGLE_PARTY);
 	}
 
 	ast_copy_string(blind_data.exten, exten, sizeof(blind_data.exten));
@@ -5085,6 +5326,30 @@
 /* BUGBUG Why doesn't this function return success/failure? */
 	ast_bridge_channel_queue_action_data(transferee_bridge_channel,
 			AST_BRIDGE_ACTION_BLIND_TRANSFER, &blind_data, sizeof(blind_data));
+
+	return 0;
+}
+
+static int bridge_channel_queue_attended_transfer(struct ast_channel *transferee,
+		struct ast_channel *unbridged_chan)
+{
+	RAII_VAR(struct ast_bridge_channel *, transferee_bridge_channel, NULL, ao2_cleanup);
+	char unbridged_chan_name[AST_CHANNEL_NAME];
+
+	ast_channel_lock(transferee);
+	transferee_bridge_channel = ast_channel_get_bridge_channel(transferee);
+	ast_channel_unlock(transferee);
+
+	if (!transferee_bridge_channel) {
+		return -1;
+	}
+
+	ast_copy_string(unbridged_chan_name, ast_channel_name(unbridged_chan),
+			sizeof(unbridged_chan_name));
+
+	ast_bridge_channel_queue_action_data(transferee_bridge_channel,
+			AST_BRIDGE_ACTION_ATTENDED_TRANSFER, unbridged_chan_name,
+			sizeof(unbridged_chan_name));
 
 	return 0;
 }
@@ -5236,27 +5501,195 @@
 	return AST_BRIDGE_TRANSFER_SUCCESS;
 }
 
+static struct ast_bridge *acquire_bridge(struct ast_channel *chan)
+{
+	struct ast_bridge *bridge;
+
+	ast_channel_lock(chan);
+	bridge = ast_channel_get_bridge(chan);
+	ast_channel_unlock(chan);
+
+	if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)) {
+		ao2_ref(bridge, -1);
+		bridge = NULL;
+	}
+
+	return bridge;
+}
+
+/*!
+ * \internal
+ * \brief Performs an attended transfer by moving a channel from one bridge to another
+ *
+ * The channel that is bridged to the source_channel is moved into the dest_bridge from
+ * the source_bridge_channel's bridge. The swap_channel is swapped out of the dest_bridge and placed in
+ * the source_bridge_channel's bridge.
+ *
+ * \note dest_bridge and source_bridge_channel's bridge MUST be locked before calling this function.
+ *
+ * \param dest_bridge The final bridge for the attended transfer
+ * \param source_channel Channel who is bridged to the channel that will move
+ * \param swap_channel Channel to be swapped out of the dest_bridge
+ * \return The success or failure of the swap attempt
+ */
+static enum ast_transfer_result bridge_swap_attended_transfer(struct ast_bridge *dest_bridge,
+		struct ast_bridge_channel *source_bridge_channel, struct ast_channel *swap_channel)
+{
+	struct ast_bridge_channel *bridged_to_source;
+
+	bridged_to_source = ast_bridge_channel_peer(source_bridge_channel);
+	if (bridged_to_source && bridged_to_source->state == AST_BRIDGE_CHANNEL_STATE_WAIT
+			&& !ast_test_flag(&bridged_to_source->features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE)) {
+		bridged_to_source->swap = swap_channel;
+		return bridge_move_do(dest_bridge, bridged_to_source, 1) ?
+			AST_BRIDGE_TRANSFER_FAIL : AST_BRIDGE_TRANSFER_SUCCESS;
+	} else {
+		return AST_BRIDGE_TRANSFER_INVALID;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Function that performs an attended transfer when both transferer channels are bridged
+ *
+ * The method by which the transfer is performed is dependent on whether the bridges allow for
+ * optimization to occur between them. If no optimization is permitted, then an unreal channel
+ * is placed as a link between the two bridges. If optimization is permitted, then that means
+ * we are free to perform move or merge operations in order to perform the transfer.
+ *
+ * \note to_transferee_bridge and to_target_bridge MUST be locked before calling this function
+ *
+ * \param to_transferee The channel that is bridged to the transferee
+ * \param to_transferee_bridge_channel to_transferee's bridge_channel
+ * \param to_transfer_target The channel that is bridged to the transfer target
+ * \param to_target_bridge_channel to_transfer_target's bridge_channel
+ * \param to_transferee_bridge The bridge between to_transferee and the transferee
+ * \param to_target_bridge The bridge between to_transfer_target and the transfer_target
+ * \return The success or failure of the attended transfer
+ */
+static enum ast_transfer_result two_bridge_attended_transfer(struct ast_channel *to_transferee,
+		struct ast_bridge_channel *to_transferee_bridge_channel,
+		struct ast_channel *to_transfer_target,
+		struct ast_bridge_channel *to_target_bridge_channel,
+		struct ast_bridge *to_transferee_bridge, struct ast_bridge *to_target_bridge)
+{
+	struct ast_bridge_channel *kick_me[] = {
+			to_transferee_bridge_channel,
+			to_target_bridge_channel,
+	};
+
+	switch (ast_bridges_allow_optimization(to_transferee_bridge, to_target_bridge)) {
+	case AST_BRIDGE_OPTIMIZE_SWAP_TO_CHAN_BRIDGE:
+		return bridge_swap_attended_transfer(to_transferee_bridge, to_target_bridge_channel, to_transferee);
+	case AST_BRIDGE_OPTIMIZE_SWAP_TO_PEER_BRIDGE:
+		return bridge_swap_attended_transfer(to_target_bridge, to_transferee_bridge_channel, to_transfer_target);
+	case AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE:
+		bridge_merge_do(to_transferee_bridge, to_target_bridge, kick_me, ARRAY_LEN(kick_me));

[... 1020 lines stripped ...]



More information about the asterisk-commits mailing list