[svn-commits] rmudgett: branch rmudgett/bridge_phase r381344 - in /team/rmudgett/bridge_pha...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Tue Feb 12 20:14:48 CST 2013


Author: rmudgett
Date: Tue Feb 12 20:14:44 2013
New Revision: 381344

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=381344
Log:
Worked out semantics for an after bridge goto location channel property.

* Make Dial, Queue, Bridge F() option setup an after bridge goto location.

Next up need to make bridging code self manage the bridge and do some
testing.

Modified:
    team/rmudgett/bridge_phase/apps/app_dial.c
    team/rmudgett/bridge_phase/apps/app_queue.c
    team/rmudgett/bridge_phase/funcs/func_channel.c
    team/rmudgett/bridge_phase/include/asterisk/bridging.h
    team/rmudgett/bridge_phase/main/bridging.c
    team/rmudgett/bridge_phase/main/features.c

Modified: team/rmudgett/bridge_phase/apps/app_dial.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/apps/app_dial.c?view=diff&rev=381344&r1=381343&r2=381344
==============================================================================
--- team/rmudgett/bridge_phase/apps/app_dial.c (original)
+++ team/rmudgett/bridge_phase/apps/app_dial.c Tue Feb 12 20:14:44 2013
@@ -67,6 +67,7 @@
 #include "asterisk/ccss.h"
 #include "asterisk/indications.h"
 #include "asterisk/framehook.h"
+#include "asterisk/bridging.h"
 
 /*** DOCUMENTATION
 	<application name="Dial" language="en_US">
@@ -2054,6 +2055,40 @@
 	return res;
 }
 
+/*!
+ * \internal
+ * \brief Setup the after bridge goto location on the peer.
+ * \since 12.0.0
+ *
+ * \param chan Calling channel for bridge.
+ * \param peer Peer channel for bridge.
+ * \param opts Dialing option flags.
+ * \param opt_args Dialing option argument strings.
+ *
+ * \return Nothing
+ */
+static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_channel *peer, struct ast_flags64 *opts, char *opt_args[])
+{
+	const char *context;
+	const char *extension;
+	int priority;
+
+	if (ast_test_flag64(opts, OPT_PEER_H)) {
+		ast_channel_lock(chan);
+		context = ast_strdupa(ast_channel_context(chan));
+		ast_channel_unlock(chan);
+		ast_after_bridge_set_h(peer, context);
+	} else if (ast_test_flag64(opts, OPT_CALLEE_GO_ON)) {
+		ast_channel_lock(chan);
+		context = ast_strdupa(ast_channel_context(chan));
+		extension = ast_strdupa(ast_channel_exten(chan));
+		priority = ast_channel_priority(chan);
+		ast_channel_unlock(chan);
+		ast_after_bridge_set_go_on(peer, context, extension, priority,
+			opt_args[OPT_ARG_CALLEE_GO_ON]);
+	}
+}
+
 static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast_flags64 *peerflags, int *continue_exec)
 {
 	int res = -1; /* default: error */
@@ -2989,6 +3024,14 @@
 		}
 
 		if (res) { /* some error */
+			if (!ast_check_hangup(chan) && ast_check_hangup(peer)) {
+				ast_channel_hangupcause_set(chan, ast_channel_hangupcause(peer));
+			}
+			setup_peer_after_bridge_goto(chan, peer, &opts, opt_args);
+			if (ast_after_bridge_goto_setup(peer)
+				|| ast_pbx_start(peer)) {
+				ast_autoservice_chan_hangup_peer(chan, peer);
+			}
 			res = -1;
 		} else {
 			if (ast_test_flag64(peerflags, OPT_CALLEE_TRANSFER))
@@ -3042,39 +3085,10 @@
 
 				ast_channel_setoption(chan, AST_OPTION_OPRMODE, &oprmode, sizeof(oprmode), 0);
 			}
-/* BUGBUG need to determine where peer is going to execute on bridge completion. */
+/* BUGBUG bridge needs to set hangup cause on chan when peer breaks the bridge. */
+			setup_peer_after_bridge_goto(chan, peer, &opts, opt_args);
 			res = ast_bridge_call(chan, peer, &config);
 		}
-
-		ast_channel_context_set(peer, ast_channel_context(chan));
-
-		if (ast_test_flag64(&opts, OPT_PEER_H)
-			&& ast_exists_extension(peer, ast_channel_context(peer), "h", 1,
-				S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
-			ast_autoservice_start(chan);
-			ast_pbx_h_exten_run(peer, ast_channel_context(peer));
-			ast_autoservice_stop(chan);
-		}
-		if (!ast_check_hangup(peer)) {
-			if (ast_test_flag64(&opts, OPT_CALLEE_GO_ON)) {
-				int goto_res;
-
-				if (!ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) {
-					ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]);
-					goto_res = ast_parseable_goto(peer, opt_args[OPT_ARG_CALLEE_GO_ON]);
-				} else { /* F() */
-					goto_res = ast_goto_if_exists(peer, ast_channel_context(chan),
-						ast_channel_exten(chan), ast_channel_priority(chan) + 1);
-				}
-				if (!goto_res && !ast_pbx_start(peer)) {
-					/* The peer is now running its own PBX. */
-					goto out;
-				}
-			}
-		} else if (!ast_check_hangup(chan)) {
-			ast_channel_hangupcause_set(chan, ast_channel_hangupcause(peer));
-		}
-		ast_autoservice_chan_hangup_peer(chan, peer);
 	}
 out:
 	if (moh) {

Modified: team/rmudgett/bridge_phase/apps/app_queue.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/apps/app_queue.c?view=diff&rev=381344&r1=381343&r2=381344
==============================================================================
--- team/rmudgett/bridge_phase/apps/app_queue.c (original)
+++ team/rmudgett/bridge_phase/apps/app_queue.c Tue Feb 12 20:14:44 2013
@@ -105,6 +105,7 @@
 #include "asterisk/callerid.h"
 #include "asterisk/cel.h"
 #include "asterisk/data.h"
+#include "asterisk/bridging.h"
 
 /* Define, to debug reference counts on queues, without debugging reference counts on queue members */
 /* #define REF_DEBUG_ONLY_QUEUES */
@@ -4921,6 +4922,7 @@
 	TRANSFER
 };
 
+#if 0	// BUGBUG
 /*! \brief Send out AMI message with member call completion status information */
 static void send_agent_complete(const struct queue_ent *qe, const char *queuename,
 	const struct ast_channel *peer, const struct member *member, time_t callstart,
@@ -4984,6 +4986,7 @@
 		(long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
 		qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
 }
+#endif	// BUGBUG
 
 struct queue_transfer_ds {
 	struct queue_ent *qe;
@@ -5038,6 +5041,7 @@
 	}
 }
 
+#if 0	// BUGBUG
 /*! \brief mechanism to tell if a queue caller was atxferred by a queue member.
  *
  * When a caller is atxferred, then the queue_transfer_info datastore
@@ -5050,6 +5054,7 @@
 {
 	return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
 }
+#endif	// BUGBUG
 
 /*! \brief create a datastore for storing relevant info to log attended transfers in the queue_log
  */
@@ -5104,6 +5109,35 @@
 		set_queue_variables(q, chan);
 		/* This unrefs the reference we made in try_calling when we allocated qeb */
 		queue_t_unref(q, "Expire bridge_config reference");
+	}
+}
+
+/*!
+ * \internal
+ * \brief Setup the after bridge goto location on the peer.
+ * \since 12.0.0
+ *
+ * \param chan Calling channel for bridge.
+ * \param peer Peer channel for bridge.
+ * \param opts Dialing option flags.
+ * \param opt_args Dialing option argument strings.
+ *
+ * \return Nothing
+ */
+static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_channel *peer, struct ast_flags *opts, char *opt_args[])
+{
+	const char *context;
+	const char *extension;
+	int priority;
+
+	if (ast_test_flag(opts, OPT_CALLEE_GO_ON)) {
+		ast_channel_lock(chan);
+		context = ast_strdupa(ast_channel_context(chan));
+		extension = ast_strdupa(ast_channel_exten(chan));
+		priority = ast_channel_priority(chan);
+		ast_channel_unlock(chan);
+		ast_after_bridge_set_go_on(peer, context, extension, priority,
+			opt_args[OPT_ARG_CALLEE_GO_ON]);
 	}
 }
 
@@ -5137,7 +5171,7 @@
  * \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
  * \param[in] ringing 1 if the 'r' option is set, otherwise 0
  */
-static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char **opt_args, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
+static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_args, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
 {
 	struct member *cur;
 	struct callattempt *outgoing = NULL; /* the list of calls we are building */
@@ -5443,11 +5477,6 @@
 			}
 		}
 	} else { /* peer is valid */
-		/* These variables are used with the F option without arguments (callee jumps to next priority after queue) */
-		char *caller_context;
-		char *caller_extension;
-		int caller_priority;
-
 		/* Ah ha!  Someone answered within the desired timeframe.  Of course after this
 		   we will always return with -1 so that it is hung up properly after the
 		   conversation.  */
@@ -5601,11 +5630,8 @@
 		set_queue_variables(qe->parent, qe->chan);
 		set_queue_variables(qe->parent, peer);
 
+		setup_peer_after_bridge_goto(qe->chan, peer, &opts, opt_args);
 		ast_channel_lock(qe->chan);
-		/* Copy next destination data for 'F' option (no args) */
-		caller_context = ast_strdupa(ast_channel_context(qe->chan));
-		caller_extension = ast_strdupa(ast_channel_exten(qe->chan));
-		caller_priority = ast_channel_priority(qe->chan);
 		if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
 				monitorfilename = ast_strdupa(monitorfilename);
 		}
@@ -5887,9 +5913,10 @@
 
 		time(&callstart);
 		transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
-/* BUGBUG need to determine where peer is going to execute on bridge completion. */
 		bridge = ast_bridge_call(qe->chan, peer, &bridge_config);
 
+/* BUGBUG need to do this queue logging a different way because we cannot reference peer anymore.  Likely needs to be made a subscriber of stasis transfer events. */
+#if 0	// BUGBUG
 		/* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
 		 * when the masquerade occurred. These other "ending" queue_log messages are unnecessary, except for
 		 * the AgentComplete manager event
@@ -5924,26 +5951,10 @@
 			/* We already logged the TRANSFER on the queue_log, but we still need to send the AgentComplete event */
 			send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
 		}
+#endif	// BUGBUG
 
 		if (transfer_ds) {
 			ast_datastore_free(transfer_ds);
-		}
-
-		if (!ast_check_hangup(peer) && ast_test_flag(&opts, OPT_CALLEE_GO_ON)) {
-			int goto_res;
-
-			if (!ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) {
-				ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]);
-				goto_res = ast_parseable_goto(peer, opt_args[OPT_ARG_CALLEE_GO_ON]);
-			} else { /* F() */
-				goto_res = ast_goto_if_exists(peer, caller_context, caller_extension,
-					caller_priority + 1);
-			}
-			if (goto_res || ast_pbx_start(peer)) {
-				ast_autoservice_chan_hangup_peer(qe->chan, peer);
-			}
-		} else {
-			ast_autoservice_chan_hangup_peer(qe->chan, peer);
 		}
 
 		res = bridge ? bridge : 1;

Modified: team/rmudgett/bridge_phase/funcs/func_channel.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/funcs/func_channel.c?view=diff&rev=381344&r1=381343&r2=381344
==============================================================================
--- team/rmudgett/bridge_phase/funcs/func_channel.c (original)
+++ team/rmudgett/bridge_phase/funcs/func_channel.c Tue Feb 12 20:14:44 2013
@@ -329,6 +329,11 @@
 	</function>
  ***/
 
+/*
+ * BUGBUG add CHANNEL(after_bridge_goto)=<parseable-goto> Sets an after bridge goto datastore property on the channel.
+ * CHANNEL(after_bridge_goto)=<empty> Deletes any after bridge goto datastore property on the channel.
+ */
+
 #define locked_copy_string(chan, dest, source, len) \
 	do { \
 		ast_channel_lock(chan); \

Modified: team/rmudgett/bridge_phase/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/include/asterisk/bridging.h?view=diff&rev=381344&r1=381343&r2=381344
==============================================================================
--- team/rmudgett/bridge_phase/include/asterisk/bridging.h (original)
+++ team/rmudgett/bridge_phase/include/asterisk/bridging.h Tue Feb 12 20:14:44 2013
@@ -640,6 +640,100 @@
  */
 void ast_bridge_remove_video_src(struct ast_bridge *bridge, struct ast_channel *chan);
 
+/*!
+ * \brief Set channel to goto specific location after the bridge.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup after bridge goto location.
+ * \param context Context to goto after bridge.
+ * \param exten Exten to goto after bridge.
+ * \param priority Priority to goto after bridge.
+ *
+ * \details Add a channel datastore to setup the goto location
+ * when the channel leaves the bridge and run a PBX from there.
+ *
+ * \return Nothing
+ */
+void ast_after_bridge_set_goto(struct ast_channel *chan, const char *context, const char *exten, int priority);
+
+/*!
+ * \brief Set channel to run the h exten after the bridge.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup after bridge goto location.
+ * \param context Context to goto after bridge.
+ *
+ * \details Add a channel datastore to setup the goto location
+ * when the channel leaves the bridge and run a PBX from there.
+ *
+ * \return Nothing
+ */
+void ast_after_bridge_set_h(struct ast_channel *chan, const char *context);
+
+/*!
+ * \brief Set channel to go on in the dialplan after the bridge.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup after bridge goto location.
+ * \param context Current context of the caller channel.
+ * \param exten Current exten of the caller channel.
+ * \param priority Current priority of the caller channel
+ * \param parseable_goto User specified goto string from dialplan.
+ *
+ * \details Add a channel datastore to setup the goto location
+ * when the channel leaves the bridge and run a PBX from there.
+ *
+ * If parseable_goto then use the given context/exten/priority
+ *   as the relative position for the parseable_goto.
+ * Else goto the given context/exten/priority+1.
+ *
+ * \return Nothing
+ */
+void ast_after_bridge_set_go_on(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *parseable_goto);
+
+/*!
+ * \brief Setup any after bridge goto location to begin execution.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup after bridge goto location.
+ *
+ * \details Pull off any after bridge goto location datastore and
+ * setup for dialplan execution there.
+ *
+ * \retval 0 on success.  The goto location is set for a PBX to run it.
+ * \retval non-zero on error or no goto location.
+ *
+ * \note If the after bridge goto is set to run an h exten it is
+ * run here immediately.
+ */
+int ast_after_bridge_goto_setup(struct ast_channel *chan);
+
+/*!
+ * \brief Run a PBX on any after bridge goto location.
+ * \since 12.0.0
+ *
+ * \param chan Channel to execute after bridge goto location.
+ *
+ * \details Pull off any after bridge goto location datastore
+ * and run a PBX at that location.
+ *
+ * \note On return, the chan pointer is no longer valid because
+ * the channel has hung up.
+ *
+ * \return Nothing
+ */
+void ast_after_bridge_goto_run(struct ast_channel *chan);
+
+/*!
+ * \brief Discard channel after bridge goto location.
+ * \since 12.0.0
+ *
+ * \param chan Channel to discard after bridge goto location.
+ *
+ * \return Nothing
+ */
+void ast_after_bridge_goto_discard(struct ast_channel *chan);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif

Modified: team/rmudgett/bridge_phase/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/main/bridging.c?view=diff&rev=381344&r1=381343&r2=381344
==============================================================================
--- team/rmudgett/bridge_phase/main/bridging.c (original)
+++ team/rmudgett/bridge_phase/main/bridging.c Tue Feb 12 20:14:44 2013
@@ -45,6 +45,7 @@
 #include "asterisk/file.h"
 #include "asterisk/module.h"
 #include "asterisk/astobj2.h"
+#include "asterisk/pbx.h"
 #include "asterisk/test.h"
 
 #include "asterisk/heap.h"
@@ -1503,6 +1504,286 @@
 	return bridge_channel;
 }
 
+struct after_bridge_goto_ds {
+	/*! Goto string that can be parsed by ast_parseable_goto(). */
+	const char *parseable_goto;
+	/*! Specific goto context or default context for parseable_goto. */
+	const char *context;
+	/*! Specific goto exten or default exten for parseable_goto. */
+	const char *exten;
+	/*! Specific goto priority or default priority for parseable_goto. */
+	int priority;
+	/*! TRUE if the peer should run the h exten. */
+	unsigned int run_h_exten:1;
+	/*! Specific goto location */
+	unsigned int specific:1;
+};
+
+/*!
+ * \internal
+ * \brief Destroy the after bridge goto datastore.
+ * \since 12.0.0
+ *
+ * \param data After bridge goto data to destroy.
+ *
+ * \return Nothing
+ */
+static void after_bridge_goto_destroy(void *data)
+{
+	struct after_bridge_goto_ds *after_bridge = data;
+
+	ast_free((char *) after_bridge->parseable_goto);
+	ast_free((char *) after_bridge->context);
+	ast_free((char *) after_bridge->exten);
+}
+
+/*!
+ * \internal
+ * \brief Fixup the after bridge goto datastore.
+ * \since 12.0.0
+ *
+ * \param data After bridge goto data to fixup.
+ * \param old_chan The datastore is moving from this channel.
+ * \param new_chan The datastore is moving to this channel.
+ *
+ * \return Nothing
+ */
+static void after_bridge_goto_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+	/* There can be only one.  Discard any already on the new channel. */
+	ast_after_bridge_goto_discard(new_chan);
+}
+
+static const struct ast_datastore_info after_bridge_goto_info = {
+	.type = "after-bridge-goto",
+	.destroy = after_bridge_goto_destroy,
+	.chan_fixup = after_bridge_goto_fixup,
+};
+
+/*!
+ * \internal
+ * \brief Remove channel goto location after the bridge and return it.
+ * \since 12.0.0
+ *
+ * \param chan Channel to remove after bridge goto location.
+ *
+ * \retval datastore on success.
+ * \retval NULL on error or not found.
+ */
+static struct ast_datastore *after_bridge_goto_remove(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore;
+
+	ast_channel_lock(chan);
+	datastore = ast_channel_datastore_find(chan, &after_bridge_goto_info, NULL);
+	if (datastore && ast_channel_datastore_remove(chan, datastore)) {
+		datastore = NULL;
+	}
+	ast_channel_unlock(chan);
+
+	return datastore;
+}
+
+void ast_after_bridge_goto_discard(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore;
+
+	datastore = after_bridge_goto_remove(chan);
+	if (datastore) {
+		ast_datastore_free(datastore);
+	}
+}
+
+int ast_after_bridge_goto_setup(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore;
+	struct after_bridge_goto_ds *after_bridge;
+	int goto_failed = -1;
+
+	/* Determine if we are going to setup a dialplan location and where. */
+	if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
+		/* An async goto has already setup a location. */
+		ast_channel_clear_softhangup(chan, AST_SOFTHANGUP_ASYNCGOTO);
+		if (!ast_check_hangup(chan)) {
+			goto_failed = 0;
+		}
+		return goto_failed;
+	}
+
+	/* Get after bridge goto datastore. */
+	datastore = after_bridge_goto_remove(chan);
+	if (!datastore) {
+		return goto_failed;
+	}
+
+	after_bridge = datastore->data;
+	if (after_bridge->run_h_exten) {
+		if (ast_exists_extension(chan, after_bridge->context, "h", 1,
+			S_COR(ast_channel_caller(chan)->id.number.valid,
+				ast_channel_caller(chan)->id.number.str, NULL))) {
+			ast_debug(1, "Running after bridge goto h exten %s,h,1\n",
+				ast_channel_context(chan));
+			ast_pbx_h_exten_run(chan, after_bridge->context);
+		}
+	} else if (!ast_check_hangup(chan)) {
+		if (after_bridge->specific) {
+			goto_failed = ast_explicit_goto(chan, after_bridge->context,
+				after_bridge->exten, after_bridge->priority);
+		} else if (!ast_strlen_zero(after_bridge->parseable_goto)) {
+			char *context;
+			char *exten;
+			int priority;
+
+			/* Option F(x) for Bridge(), Dial(), and Queue() */
+
+			/* Save current dialplan location in case of failure. */
+			context = ast_strdupa(ast_channel_context(chan));
+			exten = ast_strdupa(ast_channel_exten(chan));
+			priority = ast_channel_priority(chan);
+
+			/* Set current dialplan position to default dialplan position */
+			ast_explicit_goto(chan, after_bridge->context, after_bridge->exten,
+				after_bridge->priority);
+
+			/* Then perform the goto */
+			goto_failed = ast_parseable_goto(chan, after_bridge->parseable_goto);
+			if (goto_failed) {
+				/* Restore original dialplan location. */
+				ast_channel_context_set(chan, context);
+				ast_channel_exten_set(chan, exten);
+				ast_channel_priority_set(chan, priority);
+			}
+		} else {
+			/* Option F() for Bridge(), Dial(), and Queue() */
+			goto_failed = ast_goto_if_exists(chan, after_bridge->context,
+				after_bridge->exten, after_bridge->priority + 1);
+		}
+		if (!goto_failed) {
+			ast_debug(1, "Setup after bridge goto location to %s,%s,%d.\n",
+				ast_channel_context(chan),
+				ast_channel_exten(chan),
+				ast_channel_priority(chan));
+		}
+	}
+
+	/* Discard after bridge goto datastore. */
+	ast_datastore_free(datastore);
+
+	return goto_failed;
+}
+
+void ast_after_bridge_goto_run(struct ast_channel *chan)
+{
+	int goto_failed;
+
+	goto_failed = ast_after_bridge_goto_setup(chan);
+	if (goto_failed || ast_pbx_run(chan)) {
+		ast_hangup(chan);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Set after bridge goto location of channel.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup after bridge goto location.
+ * \param run_h_exten TRUE if the h exten should be run.
+ * \param specific TRUE if the context/exten/priority is exactly specified.
+ * \param context Context to goto after bridge.
+ * \param exten Exten to goto after bridge. (Could be NULL if run_h_exten)
+ * \param priority Priority to goto after bridge.
+ * \param parseable_goto User specified goto string. (Could be NULL)
+ *
+ * \details Add a channel datastore to setup the goto location
+ * when the channel leaves the bridge and run a PBX from there.
+ *
+ * If run_h_exten then execute the h exten found in the given context.
+ * Else if specific then goto the given context/exten/priority.
+ * Else if parseable_goto then use the given context/exten/priority
+ *   as the relative position for the parseable_goto.
+ * Else goto the given context/exten/priority+1.
+ *
+ * \return Nothing
+ */
+static void __after_bridge_set_goto(struct ast_channel *chan, int run_h_exten, int specific, const char *context, const char *exten, int priority, const char *parseable_goto)
+{
+	struct ast_datastore *datastore;
+	struct after_bridge_goto_ds *after_bridge;
+
+	/* Sanity checks. */
+	ast_assert(chan != NULL);
+	if (!chan) {
+		return;
+	}
+	if (run_h_exten) {
+		ast_assert(run_h_exten && context);
+		if (!context) {
+			return;
+		}
+	} else {
+		ast_assert(context && exten && 0 < priority);
+		if (!context || !exten || priority < 1) {
+			return;
+		}
+	}
+
+	/* Create a new datastore. */
+	datastore = ast_datastore_alloc(&after_bridge_goto_info, NULL);
+	if (!datastore) {
+		return;
+	}
+	after_bridge = ast_calloc(1, sizeof(*after_bridge));
+	if (!after_bridge) {
+		ast_datastore_free(datastore);
+		return;
+	}
+
+	/* Initialize it. */
+	after_bridge->parseable_goto = ast_strdup(parseable_goto);
+	after_bridge->context = ast_strdup(context);
+	after_bridge->exten = ast_strdup(exten);
+	after_bridge->priority = priority;
+	after_bridge->run_h_exten = run_h_exten ? 1 : 0;
+	after_bridge->specific = specific ? 1 : 0;
+	datastore->data = after_bridge;
+	if ((parseable_goto && !after_bridge->parseable_goto)
+		|| (context && !after_bridge->context)
+		|| (exten && !after_bridge->exten)) {
+		ast_datastore_free(datastore);
+		return;
+	}
+
+	/* Put it on the channel replacing any existing one. */
+	ast_channel_lock(chan);
+	ast_after_bridge_goto_discard(chan);
+	ast_channel_datastore_add(chan, datastore);
+	ast_channel_unlock(chan);
+}
+
+void ast_after_bridge_set_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
+{
+	__after_bridge_set_goto(chan, 0, 1, context, exten, priority, NULL);
+}
+
+void ast_after_bridge_set_h(struct ast_channel *chan, const char *context)
+{
+	__after_bridge_set_goto(chan, 1, 0, context, NULL, 1, NULL);
+}
+
+void ast_after_bridge_set_go_on(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *parseable_goto)
+{
+	char *p_goto;
+
+	if (!ast_strlen_zero(parseable_goto)) {
+		p_goto = ast_strdupa(parseable_goto);
+		ast_replace_subargument_delimiter(p_goto);
+	} else {
+		p_goto = NULL;
+	}
+	__after_bridge_set_goto(chan, 0, 0, context, exten, priority, p_goto);
+}
+
 enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge,
 	struct ast_channel *chan,
 	struct ast_channel *swap,
@@ -1519,7 +1800,8 @@
 		ao2_ref(bridge, -1);
 	}
 	if (!bridge_channel) {
-		return AST_BRIDGE_CHANNEL_STATE_HANGUP;
+		state = AST_BRIDGE_CHANNEL_STATE_HANGUP;
+		goto join_exit;
 	}
 	if (tech_args) {
 		bridge_channel->tech_args = *tech_args;
@@ -1540,6 +1822,15 @@
 	bridge_channel->features = NULL;
 
 	ao2_ref(bridge_channel, -1);
+
+join_exit:;
+	if (!(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO)
+		&& !ast_after_bridge_goto_setup(chan)) {
+		/* Claim the after bridge goto is an async goto destination. */
+		ast_channel_lock(chan);
+		ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
+		ast_channel_unlock(chan);
+	}
 	return state;
 }
 
@@ -1558,6 +1849,8 @@
 	bridge_channel->swap = NULL;
 	ast_bridge_features_destroy(bridge_channel->features);
 	bridge_channel->features = NULL;
+
+	ast_after_bridge_goto_discard(bridge_channel->chan);
 
 	return NULL;
 }
@@ -1585,9 +1878,6 @@
 
 	ao2_ref(bridge_channel, -1);
 
-/* BUGBUG need to run a PBX on this channel or hangup. */
-/* BUGBUG need to start the PBX at the appropriate location. */
-/* BUGBUG need to determine where to execute in the dialplan. */
 	switch (state) {
 	case AST_BRIDGE_CHANNEL_STATE_DEPART:
 	case AST_BRIDGE_CHANNEL_STATE_DEPART_END:
@@ -1598,10 +1888,10 @@
 	case AST_BRIDGE_CHANNEL_STATE_HANGUP:
 	case AST_BRIDGE_CHANNEL_STATE_END:
 	default:
-		ast_hangup(chan);
 		break;
 	}
 
+	ast_after_bridge_goto_run(chan);
 	return NULL;
 }
 
@@ -1625,9 +1915,15 @@
 
 	/* Actually create the thread that will handle the channel */
 	if (independent) {
+		/* Independently imparted channels cannot have a PBX. */
+		ast_assert(!ast_channel_pbx(chan));
+
 		res = ast_pthread_create_detached(&bridge_channel->thread, NULL,
 			bridge_channel_ind_thread, bridge_channel);
 	} else {
+		/* Imparted channels to be departed should not have a PBX either. */
+		ast_assert(!ast_channel_pbx(chan));
+
 		res = ast_pthread_create(&bridge_channel->thread, NULL,
 			bridge_channel_depart_thread, bridge_channel);
 	}

Modified: team/rmudgett/bridge_phase/main/features.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/main/features.c?view=diff&rev=381344&r1=381343&r2=381344
==============================================================================
--- team/rmudgett/bridge_phase/main/features.c (original)
+++ team/rmudgett/bridge_phase/main/features.c Tue Feb 12 20:14:44 2013
@@ -1142,18 +1142,16 @@
 		ast_channel_data_set(tobj->peer, "(Empty)");
 	}
 
-/* BUGBUG If tobj->return_to_pbx need to setup datastore where peer is going to execute on bridge completion. */
+	if (tobj->return_to_pbx) {
+		ast_after_bridge_set_goto(tobj->chan, ast_channel_context(tobj->chan),
+			ast_channel_exten(tobj->chan), ast_channel_priority(tobj->chan));
+		ast_after_bridge_set_goto(tobj->peer, ast_channel_context(tobj->peer),
+			ast_channel_exten(tobj->peer), ast_channel_priority(tobj->peer));
+	}
+
 	ast_bridge_call(tobj->chan, tobj->peer, &tobj->bconfig);
 
-	if (tobj->return_to_pbx && !ast_check_hangup(tobj->chan)) {
-		ast_log(LOG_VERBOSE, "putting chan %s into PBX again\n", ast_channel_name(tobj->chan));
-		if (ast_pbx_run(tobj->chan)) {
-			ast_log(LOG_WARNING, "FAILED continuing PBX on chan %s\n", ast_channel_name(tobj->chan));
-			ast_hangup(tobj->chan);
-		}
-	} else {
-		ast_hangup(tobj->chan);
-	}
+	ast_after_bridge_goto_run(tobj->chan);
 
 	ast_free(tobj);
 
@@ -7759,12 +7757,17 @@
  */
 static int bridge_exec(struct ast_channel *chan, const char *data)
 {
-	struct ast_channel *current_dest_chan, *final_dest_chan, *chans[2];
+	struct ast_channel *current_dest_chan;
+	struct ast_channel *final_dest_chan;
+	struct ast_channel *chans[2];
 	char *tmp_data  = NULL;
 	struct ast_flags opts = { 0, };
 	struct ast_bridge_config bconfig = { { 0, }, };
 	char *opt_args[OPT_ARG_ARRAY_SIZE];
 	struct timeval calldurationlimit = { 0, };
+	const char *context;
+	const char *extension;
+	int priority;
 
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(dest_chan);
@@ -7923,56 +7926,28 @@
 	if (ast_test_flag(&opts, OPT_CALLER_PARK))
 		ast_set_flag(&(bconfig.features_caller), AST_FEATURE_PARKCALL);
 
-/* BUGBUG need to determine where peer is going to execute on bridge completion. */
+	/* Setup after bridge goto location. */
+	if (ast_test_flag(&opts, OPT_CALLEE_GO_ON)) {
+		ast_channel_lock(chan);
+		context = ast_strdupa(ast_channel_context(chan));
+		extension = ast_strdupa(ast_channel_exten(chan));
+		priority = ast_channel_priority(chan);
+		ast_channel_unlock(chan);
+		ast_after_bridge_set_go_on(final_dest_chan, context, extension, priority,
+			opt_args[OPT_ARG_CALLEE_GO_ON]);
+	} else if (!ast_test_flag(&opts, OPT_CALLEE_KILL)) {
+		ast_channel_lock(final_dest_chan);
+		context = ast_strdupa(ast_channel_context(final_dest_chan));
+		extension = ast_strdupa(ast_channel_exten(final_dest_chan));
+		priority = ast_channel_priority(final_dest_chan);
+		ast_channel_unlock(final_dest_chan);
+		ast_after_bridge_set_goto(final_dest_chan, context, extension, priority);
+	}
+
 	ast_bridge_call(chan, final_dest_chan, &bconfig);
 
 	/* The bridge has ended, set BRIDGERESULT to SUCCESS. */
 	pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "SUCCESS");
-
-	/* If the other channel has not been hung up, return it to the PBX */
-	if (!ast_check_hangup(final_dest_chan)) {
-		if (ast_test_flag(&opts, OPT_CALLEE_GO_ON)) {
-			char *caller_context;
-			char *caller_extension;
-			int caller_priority;
-			int goto_opt;
-
-			ast_channel_lock(chan);
-			caller_context = ast_strdupa(ast_channel_context(chan));
-			caller_extension = ast_strdupa(ast_channel_exten(chan));
-			caller_priority = ast_channel_priority(chan);
-			ast_channel_unlock(chan);
-
-			if (!ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) {
-				ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]);
-				/* Set current dialplan position to bridger dialplan position */
-				goto_opt = ast_goto_if_exists(final_dest_chan, caller_context, caller_extension, caller_priority)
-					/* Then perform the goto */
-					|| ast_parseable_goto(final_dest_chan, opt_args[OPT_ARG_CALLEE_GO_ON]);
-			} else { /* F() */
-				goto_opt = ast_goto_if_exists(final_dest_chan, caller_context, caller_extension, caller_priority + 1);
-			}
-			if (goto_opt || ast_pbx_start(final_dest_chan)) {
-				ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
-			}
-		} else if (!ast_test_flag(&opts, OPT_CALLEE_KILL)) {
-			ast_debug(1, "starting new PBX in %s,%s,%d for chan %s\n",
-				ast_channel_context(final_dest_chan), ast_channel_exten(final_dest_chan),
-				ast_channel_priority(final_dest_chan), ast_channel_name(final_dest_chan));
-
-			if (ast_pbx_start(final_dest_chan)) {
-				ast_log(LOG_WARNING, "FAILED continuing PBX on dest chan %s\n", ast_channel_name(final_dest_chan));
-				ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
-			} else {
-				ast_debug(1, "SUCCESS continuing PBX on chan %s\n", ast_channel_name(final_dest_chan));
-			}
-		} else {
-			ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
-		}
-	} else {
-		ast_debug(1, "chan %s was hungup\n", ast_channel_name(final_dest_chan));
-		ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
-	}
 done:
 	ast_free((char *) bconfig.warning_sound);
 	ast_free((char *) bconfig.end_sound);




More information about the svn-commits mailing list