[asterisk-commits] mmichelson: branch mmichelson/more_transfer r386936 - in /team/mmichelson/mor...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Apr 30 09:04:51 CDT 2013


Author: mmichelson
Date: Tue Apr 30 09:04:47 2013
New Revision: 386936

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=386936
Log:
Add naive attended transfer implementation.

This is missing a couple of necessary items in order to function
properly:

1) I need the ability for a channel that is removed from a bridge
to run an arbitrary callback. Right now, I've faked this by queuing
a bridge action on a bridged channel.

2) I need the "virtual" channel construct. Right now, I need a local
channel and I need to be able to do operations with both ends of it
(place the ;1 in a bridge and masquerade the ;2 into a channel). Currently
I'm not able to do this.


Modified:
    team/mmichelson/more_transfer/include/asterisk/bridging.h
    team/mmichelson/more_transfer/include/asterisk/channel.h
    team/mmichelson/more_transfer/main/bridging.c
    team/mmichelson/more_transfer/main/channel.c
    team/mmichelson/more_transfer/main/pbx.c

Modified: team/mmichelson/more_transfer/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/more_transfer/include/asterisk/bridging.h?view=diff&rev=386936&r1=386935&r2=386936
==============================================================================
--- team/mmichelson/more_transfer/include/asterisk/bridging.h (original)
+++ team/mmichelson/more_transfer/include/asterisk/bridging.h Tue Apr 30 09:04:47 2013
@@ -223,6 +223,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

Modified: team/mmichelson/more_transfer/include/asterisk/channel.h
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/more_transfer/include/asterisk/channel.h?view=diff&rev=386936&r1=386935&r2=386936
==============================================================================
--- team/mmichelson/more_transfer/include/asterisk/channel.h (original)
+++ team/mmichelson/more_transfer/include/asterisk/channel.h Tue Apr 30 09:04:47 2013
@@ -4166,4 +4166,51 @@
  */
 int ast_channel_is_bridged(const 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.
+ *
+ * \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 take the source channel and have it
+ * take the place of the destination channel. Any application running on the
+ * destination channel will now be running on the source channel instead.
+ *
+ * \note This function is NOT intended to be used on bridged channels. If you
+ * wish to move a channel into a bridge, you can use ast_bridge_impart() and
+ * ast_bridge_join() and swap a channel out if desired.
+ *
+ * \note When this function returns succesfully, the source channel is in a
+ * state where its continued use is unreliable.
+ *
+ * \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: team/mmichelson/more_transfer/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/more_transfer/main/bridging.c?view=diff&rev=386936&r1=386935&r2=386936
==============================================================================
--- team/mmichelson/more_transfer/main/bridging.c (original)
+++ team/mmichelson/more_transfer/main/bridging.c Tue Apr 30 09:04:47 2013
@@ -1969,6 +1969,30 @@
 {
 	ast_async_goto(bridge_channel->chan, blind_data->context, blind_data->exten, 1);
 	bridge_handle_hangup(bridge_channel);
+}
+
+static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_channel,
+		const char *unbridged_chan_name)
+{
+	RAII_VAR(struct ast_channel *, chan_unbridged, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_channel *, chan_bridged, NULL, ao2_cleanup);
+
+	chan_unbridged = ast_channel_get_by_name(unbridged_chan_name);
+	if (!chan_unbridged) {
+		/* Dang, it disappeared somehow */
+		return;
+	}
+
+	{
+		SCOPED_CHANNELLOCK(lock, bridge_channel);
+		chan_bridged = bridge_channel->chan;
+		if (!chan_bridged) {
+			return;
+		}
+		ao2_ref(chan_bridged, +1);
+	}
+	
+	ast_channel_move(chan_unbridged, chan_bridged);
 }
 
 /*!
@@ -2027,6 +2051,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;
 	}
@@ -4761,6 +4788,26 @@
 	return AST_BRIDGE_TRANSFER_SUCCESS;
 }
 
+static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *chan_bridged,
+		struct ast_channel *chan_unbridged, struct ast_bridge *bridge)
+{
+	/* This needs to do the following:
+	 * 1) Create a local channel
+	 * 2) Masquerade the local ;2 channel into the chan_unbridged channel
+	 * 3) Add the local ;1 channel to the bridge (swapping the chan_bridged out).
+	 *
+	 * The problem is that we've got a bit of a dicey situation with the local
+	 * channel for now. When we request a local channel, all we have is the ;1 channel.
+	 * We don't have the ability to masquerade the ;2 side the way we'd like. For now
+	 * this function is a stub;
+	 */
+
+	/* XXX For now, transferring an entire bridge to an unbridged channel is invalid until
+	 * the local channel problem described above is cleared up
+	 */
+	return AST_BRIDGE_TRANSFER_INVALID;
+}
+
 /*!
  * \internal
  * \brief Get the transferee channel
@@ -4833,6 +4880,31 @@
 	/* XXX 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];
+
+	{
+		SCOPED_LOCK(lock, transferee, ast_channel_lock, ast_channel_unlock);
+		transferee_bridge_channel = ast_channel_internal_bridge_channel(transferee);
+		if (!transferee_bridge_channel) {
+			return -1;
+		}
+		ao2_ref(transferee_bridge_channel, +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;
 }
@@ -4945,24 +5017,81 @@
 enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_transferee,
 		struct ast_channel *to_transfer_target, struct ast_framehook *hook)
 {
-	/* First, check the validity of scenario. If invalid, return AST_BRIDGE_TRANSFER_INVALID. The following are invalid:
-	 *     1) atxfer of an unbridged call to an unbridged call
-	 *     2) atxfer of an unbridged call to a multi-party (n > 2) bridge
-	 *     3) atxfer of a multi-party (n > 2) bridge to an unbridged call
-	 * Second, check that the bridge(s) involved allows transfers. If not, return AST_BRIDGE_TRANSFER_NOT_PERMITTED.
-	 * Third, break into different scenarios for different bridge situations:
-	 * If both channels are bridged, perform a bridge merge. Direction of the merge is TBD.
-	 * If channel A is bridged, and channel B is not (e.g. transferring to IVR or blond transfer)
-	 *     Some manner of masquerading is necessary. Presumably, you'd want to move channel A's bridge peer
-	 *     into where channel B is. However, it may be possible to do something a bit different, where a
-	 *     local channel is created and put into channel A's bridge. The local channel's ;2 channel
-	 *     is then masqueraded with channel B in some way.
-	 * If channel A is not bridged and channel B is, then:
-	 *     This is similar to what is done in the previous scenario. Create a local channel and place it
-	 *     into B's bridge. Then masquerade the ;2 leg of the local channel.
-	 */
-
-	/* XXX STUB */
+	RAII_VAR(struct ast_bridge *, to_transferee_bridge, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_bridge *, to_target_bridge, NULL, ao2_cleanup);
+	RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_channel *, transferee, NULL, ao2_cleanup);
+	struct ast_bridge *the_bridge;
+	struct ast_channel *chan_bridged;
+	struct ast_channel *chan_unbridged;
+	int transfer_prohibited;
+	int do_bridge_transfer;
+
+	ast_channel_lock(to_transferee);
+	to_transferee_bridge = ast_channel_get_bridge(to_transferee);
+	ast_channel_unlock(to_transferee);
+
+	ast_channel_lock(to_transfer_target);
+	to_target_bridge = ast_channel_get_bridge(to_transfer_target);
+	ast_channel_unlock(to_transfer_target);
+
+	/* They can't both be unbridged, you silly goose! */
+	if (!to_transferee_bridge && !to_target_bridge) {
+		return AST_BRIDGE_TRANSFER_INVALID;
+	}
+
+	/* Let's get the easy one out of the way first */
+	if (to_transferee_bridge && to_target_bridge) {
+		struct ast_channel *kick_chans[2] = {
+			to_transferee,
+			to_transfer_target,
+		};
+		return ast_bridge_merge(to_transferee_bridge, to_target_bridge,
+			1, kick_chans, 2) == 0 ? AST_BRIDGE_TRANSFER_SUCCESS :
+			AST_BRIDGE_TRANSFER_FAIL;
+	}
+
+	the_bridge = to_transferee_bridge ?: to_target_bridge;
+	chan_bridged = to_transferee_bridge ? to_transferee : to_transfer_target;
+	chan_unbridged = to_transferee_bridge ? to_transfer_target : to_transferee;
+
+	{
+		int chan_count;
+		SCOPED_LOCK(lock, the_bridge, ast_bridge_lock, ast_bridge_unlock);
+
+		channels = ast_bridge_peers_nolock(the_bridge);
+		if (!channels) {
+			return AST_BRIDGE_TRANSFER_FAIL;
+		}
+		chan_count = ao2_container_count(channels);
+		if (chan_count <= 1) {
+			return AST_BRIDGE_TRANSFER_INVALID;
+		}
+		transfer_prohibited = ast_test_flag(&the_bridge->feature_flags,
+				AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
+		do_bridge_transfer = ast_test_flag(&the_bridge->feature_flags,
+				AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY) ||
+				chan_count > 2;
+	}
+
+	if (transfer_prohibited) {
+		return AST_BRIDGE_TRANSFER_NOT_PERMITTED;
+	}
+
+	if (do_bridge_transfer) {
+		return attended_transfer_bridge(chan_bridged, chan_unbridged, the_bridge);
+	}
+
+	transferee = get_transferee(channels, chan_bridged);
+	if (!transferee) {
+		return AST_BRIDGE_TRANSFER_FAIL;
+	}
+
+	if (bridge_channel_queue_attended_transfer(transferee, chan_unbridged)) {
+		return AST_BRIDGE_TRANSFER_FAIL;
+	}
+
+	ast_bridge_remove(the_bridge, chan_bridged);
 	return AST_BRIDGE_TRANSFER_SUCCESS;
 }
 

Modified: team/mmichelson/more_transfer/main/channel.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/more_transfer/main/channel.c?view=diff&rev=386936&r1=386935&r2=386936
==============================================================================
--- team/mmichelson/more_transfer/main/channel.c (original)
+++ team/mmichelson/more_transfer/main/channel.c Tue Apr 30 09:04:47 2013
@@ -11315,3 +11315,70 @@
 	RAII_VAR(struct ast_bridge *, bridge, ast_channel_get_bridge(chan), ao2_cleanup);
 	return bridge != 0;
 }
+
+struct ast_channel *ast_channel_yank(struct ast_channel *yankee)
+{
+	struct ast_channel *tmpchan;
+	struct {
+		char *accountcode;
+		char *exten;
+		char *context;
+		char *linkedid;
+		char *name;
+		struct ast_cdr *cdr;
+		int amaflags;
+		int state;
+		struct ast_format readformat;
+		struct ast_format writeformat;
+	} tmpvars = { 0, };
+
+	ast_channel_lock(yankee);
+	tmpvars.accountcode = ast_strdupa(ast_channel_accountcode(yankee));
+	tmpvars.exten = ast_strdupa(ast_channel_exten(yankee));
+	tmpvars.context = ast_strdupa(ast_channel_context(yankee));
+	tmpvars.linkedid = ast_strdupa(ast_channel_linkedid(yankee));
+	tmpvars.name = ast_strdupa(ast_channel_name(yankee));
+	tmpvars.amaflags = ast_channel_amaflags(yankee);
+	tmpvars.state = ast_channel_state(yankee);
+	ast_format_copy(&tmpvars.writeformat, ast_channel_writeformat(yankee));
+	ast_format_copy(&tmpvars.readformat, ast_channel_readformat(yankee));
+	tmpvars.cdr = ast_channel_cdr(yankee) ? ast_cdr_dup(ast_channel_cdr(yankee)) : NULL;
+	ast_channel_unlock(yankee);
+
+	/* Do not hold any channel locks while calling channel_alloc() since the function
+	 * locks the channel container when linking the new channel in. */
+	if (!(tmpchan = ast_channel_alloc(0, tmpvars.state, 0, 0, tmpvars.accountcode,
+					tmpvars.exten, tmpvars.context, tmpvars.linkedid, tmpvars.amaflags,
+					"Surrogate/%s", tmpvars.name))) {
+		ast_cdr_discard(tmpvars.cdr);
+		return NULL;
+	}
+
+	/* copy the cdr info over */
+	if (tmpvars.cdr) {
+		ast_cdr_discard(ast_channel_cdr(tmpchan));
+		ast_channel_cdr_set(tmpchan, tmpvars.cdr);
+		tmpvars.cdr = NULL;
+	}
+
+	/* Make formats okay */
+	ast_format_copy(ast_channel_readformat(tmpchan), &tmpvars.readformat);
+	ast_format_copy(ast_channel_writeformat(tmpchan), &tmpvars.writeformat);
+
+	if (ast_channel_move(tmpchan, yankee)) {
+		ast_hangup(tmpchan);
+		return NULL;
+	}
+
+	return tmpchan;
+}
+
+int ast_channel_move(struct ast_channel *dest, struct ast_channel *source)
+{
+	if (ast_channel_masquerade(dest, source)) {
+		return -1;
+	}
+
+	ast_do_masquerade(dest);
+	return 0;
+}

Modified: team/mmichelson/more_transfer/main/pbx.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/more_transfer/main/pbx.c?view=diff&rev=386936&r1=386935&r2=386936
==============================================================================
--- team/mmichelson/more_transfer/main/pbx.c (original)
+++ team/mmichelson/more_transfer/main/pbx.c Tue Apr 30 09:04:47 2013
@@ -9378,20 +9378,7 @@
 
 int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
 {
-	int res = 0;
-	struct ast_channel *tmpchan;
-	struct {
-		char *accountcode;
-		char *exten;
-		char *context;
-		char *linkedid;
-		char *name;
-		struct ast_cdr *cdr;
-		int amaflags;
-		int state;
-		struct ast_format readformat;
-		struct ast_format writeformat;
-	} tmpvars = { 0, };
+	struct ast_channel *newchan;
 
 	ast_channel_lock(chan);
 	/* Channels in a bridge or running a PBX can be sent directly to the specified destination */
@@ -9402,64 +9389,19 @@
 		ast_explicit_goto(chan, context, exten, priority);
 		ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
 		ast_channel_unlock(chan);
-		return res;
-	}
-
-	/* In order to do it when the channel doesn't really exist within
-	 * the PBX, we have to make a new channel, masquerade, and start the PBX
-	 * at the new location */
-	tmpvars.accountcode = ast_strdupa(ast_channel_accountcode(chan));
-	tmpvars.exten = ast_strdupa(ast_channel_exten(chan));
-	tmpvars.context = ast_strdupa(ast_channel_context(chan));
-	tmpvars.linkedid = ast_strdupa(ast_channel_linkedid(chan));
-	tmpvars.name = ast_strdupa(ast_channel_name(chan));
-	tmpvars.amaflags = ast_channel_amaflags(chan);
-	tmpvars.state = ast_channel_state(chan);
-	ast_format_copy(&tmpvars.writeformat, ast_channel_writeformat(chan));
-	ast_format_copy(&tmpvars.readformat, ast_channel_readformat(chan));
-	tmpvars.cdr = ast_channel_cdr(chan) ? ast_cdr_dup(ast_channel_cdr(chan)) : NULL;
-
-	ast_channel_unlock(chan);
-
-	/* Do not hold any channel locks while calling channel_alloc() since the function
-	 * locks the channel container when linking the new channel in. */
-	if (!(tmpchan = ast_channel_alloc(0, tmpvars.state, 0, 0, tmpvars.accountcode, tmpvars.exten, tmpvars.context, tmpvars.linkedid, tmpvars.amaflags, "AsyncGoto/%s", tmpvars.name))) {
-		ast_cdr_discard(tmpvars.cdr);
+		return 0;
+	}
+
+	/* Otherwise, we need to gain control of the channel first */
+	newchan = ast_channel_yank(chan);
+	ast_explicit_goto(newchan, context, exten, priority);
+	if (ast_pbx_start(newchan)) {
+		ast_hangup(newchan);
+		ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast_channel_name(newchan));
 		return -1;
 	}
 
-	/* copy the cdr info over */
-	if (tmpvars.cdr) {
-		ast_cdr_discard(ast_channel_cdr(tmpchan));
-		ast_channel_cdr_set(tmpchan, tmpvars.cdr);
-		tmpvars.cdr = NULL;
-	}
-
-	/* Make formats okay */
-	ast_format_copy(ast_channel_readformat(tmpchan), &tmpvars.readformat);
-	ast_format_copy(ast_channel_writeformat(tmpchan), &tmpvars.writeformat);
-
-	/* Setup proper location. Never hold another channel lock while calling this function. */
-	ast_explicit_goto(tmpchan, S_OR(context, tmpvars.context), S_OR(exten, tmpvars.exten), priority);
-
-	/* Masquerade into tmp channel */
-	if (ast_channel_masquerade(tmpchan, chan)) {
-		/* Failed to set up the masquerade.  It's probably chan_local
-		 * in the middle of optimizing itself out.  Sad. :( */
-		ast_hangup(tmpchan);
-		tmpchan = NULL;
-		res = -1;
-	} else {
-		ast_do_masquerade(tmpchan);
-		/* Start the PBX going on our stolen channel */
-		if (ast_pbx_start(tmpchan)) {
-			ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast_channel_name(tmpchan));
-			ast_hangup(tmpchan);
-			res = -1;
-		}
-	}
-
-	return res;
+	return 0;
 }
 
 int ast_async_goto_by_name(const char *channame, const char *context, const char *exten, int priority)




More information about the asterisk-commits mailing list