[svn-commits] mmichelson: branch mmichelson/atxfer_features r392275 - in /team/mmichelson/a...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Wed Jun 19 15:12:18 CDT 2013


Author: mmichelson
Date: Wed Jun 19 15:12:16 2013
New Revision: 392275

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=392275
Log:
Start gutting the attended transfer feature hook.

I can, with much confidence, claim that with this commit DTMF
attended transfers are now completely and utterly broken. This
is intentional though because it's just part one of putting the
building blocks together.


Modified:
    team/mmichelson/atxfer_features/include/asterisk/bridging_basic.h
    team/mmichelson/atxfer_features/main/bridging_basic.c

Modified: team/mmichelson/atxfer_features/include/asterisk/bridging_basic.h
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/atxfer_features/include/asterisk/bridging_basic.h?view=diff&rev=392275&r1=392274&r2=392275
==============================================================================
--- team/mmichelson/atxfer_features/include/asterisk/bridging_basic.h (original)
+++ team/mmichelson/atxfer_features/include/asterisk/bridging_basic.h Wed Jun 19 15:12:16 2013
@@ -99,7 +99,7 @@
 void ast_bridging_init_basic(void);
 
 /* XXX Everything below this point can likely be made private to bridging_basic.c
- * If not, then attended_transfer_properties needs an ast_ prefix and the two
+ * If not, then attended_transfer_properties needs an ast_ prefix and the
  * functions need doxygen
  */
 struct attended_transfer_properties;

Modified: team/mmichelson/atxfer_features/main/bridging_basic.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/atxfer_features/main/bridging_basic.c?view=diff&rev=392275&r1=392274&r2=392275
==============================================================================
--- team/mmichelson/atxfer_features/main/bridging_basic.c (original)
+++ team/mmichelson/atxfer_features/main/bridging_basic.c Wed Jun 19 15:12:16 2013
@@ -247,14 +247,11 @@
 }
 
 /*! \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];
+static struct ast_channel *dial_transfer(struct ast_channel *caller, const char *destination)
+{
 	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,
@@ -268,12 +265,6 @@
 
 	/* 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;
 }
@@ -309,48 +300,6 @@
 		return context;
 	}
 	return "default";
-}
-
-/*! Attended transfer code */
-enum atxfer_code {
-	/*! Party C hungup or other reason to abandon the transfer. */
-	ATXFER_INCOMPLETE,
-	/*! Transfer party C to party A. */
-	ATXFER_COMPLETE,
-	/*! Turn the transfer into a threeway call. */
-	ATXFER_THREEWAY,
-	/*! Hangup party C and return party B to the bridge. */
-	ATXFER_ABORT,
-};
-
-/*! \brief Attended transfer feature to complete transfer */
-static int attended_transfer_complete(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
-	enum atxfer_code *transfer_code = hook_pvt;
-
-	*transfer_code = ATXFER_COMPLETE;
-	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
-	return 0;
-}
-
-/*! \brief Attended transfer feature to turn it into a threeway call */
-static int attended_transfer_threeway(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
-	enum atxfer_code *transfer_code = hook_pvt;
-
-	*transfer_code = ATXFER_THREEWAY;
-	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
-	return 0;
-}
-
-/*! \brief Attended transfer feature to abort transfer */
-static int attended_transfer_abort(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
-	enum atxfer_code *transfer_code = hook_pvt;
-
-	*transfer_code = ATXFER_ABORT;
-	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
-	return 0;
 }
 
 enum attended_transfer_state {
@@ -414,6 +363,51 @@
 	int atxfercallbackretries;
 	int retry_attempts;
 };
+
+static void attended_transfer_properties_destructor(void *obj)
+{
+	struct attended_transfer_properties *props = obj;
+
+	ao2_cleanup(props->target_bridge);
+	ao2_cleanup(props->transferee_bridge);
+	ast_channel_unref(props->transferer);
+	ast_channel_unref(props->transfer_target);
+	ast_cond_destroy(&props->cond);
+	ast_mutex_destroy(&props->lock);
+	AST_LIST_HEAD_DESTROY(&props->stimulus_queue);
+}
+
+static struct attended_transfer_properties *attended_transfer_properties_alloc(
+		struct ast_bridge *transferee_bridge, struct ast_bridge *target_bridge,
+		struct ast_channel *transferer, struct ast_channel *transfer_target,
+		int atxferdropcall, int atxfernoanswertimeout, int atxfercallbackretries)
+{
+	struct attended_transfer_properties *props;
+
+	props = ao2_alloc(sizeof(*props), attended_transfer_properties_destructor);
+	if (!props) {
+		return NULL;
+	}
+	
+	ast_mutex_init(&props->lock);
+	ast_cond_init(&props->cond, NULL);
+
+	ao2_ref(transferee_bridge, +1);
+	props->transferee_bridge = transferee_bridge;
+	ao2_ref(target_bridge, +1);
+	props->target_bridge = target_bridge;
+
+	props->transferer = ast_channel_ref(transferer);
+	props->transfer_target = ast_channel_ref(transfer_target);
+
+	props->timeout = ast_samp2tv(atxfernoanswertimeout, 1000);
+	AST_LIST_HEAD_INIT(&props->stimulus_queue);
+
+	props->atxferdropcall = atxferdropcall;
+	props->atxfercallbackretries = atxfercallbackretries;
+
+	return props;
+}
 
 /*!
  * \brief enter callback for TRANSFER_CALLING_TARGET state
@@ -894,7 +888,7 @@
 	return list->stimulus;
 }
 
-static __attribute__((unused)) void *attended_transfer_monitor_thread(void *data)
+static void *attended_transfer_monitor_thread(void *data)
 {
 	struct attended_transfer_properties *props = data;
 
@@ -919,6 +913,16 @@
 
 	return NULL;
 }
+
+/* XXX Needs swap option as well */
+static int add_transferer_role(struct ast_channel *chan, const char *abort, const char *complete, const char *threeway)
+{
+	return ast_channel_add_bridge_role(chan, TRANSFERER_ROLE_NAME) ||
+		ast_channel_set_bridge_role_option(chan, TRANSFERER_ROLE_NAME, "abort", abort) ||
+		ast_channel_set_bridge_role_option(chan, TRANSFERER_ROLE_NAME, "complete", complete) ||
+		ast_channel_set_bridge_role_option(chan, TRANSFERER_ROLE_NAME, "threeway", threeway);
+}
+
 
 /*! \brief Internal built in feature for attended transfers */
 
@@ -1166,23 +1170,25 @@
  *  * Destroy all bridges
  *  * End monitoring thread
  */
-
 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);
+	int atxferdropcall;
+	int atxfercallbackretries;
+	int atxfernoanswertimeout;
+	struct attended_transfer_properties *props;
+	char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1];
+	pthread_t thread;
 
 	ast_bridge_channel_write_hold(bridge_channel, NULL);
 
@@ -1195,6 +1201,8 @@
 	if (!xfer_cfg) {
 		ast_log(LOG_ERROR, "Unable to get transfer configuration options\n");
 		ast_channel_unlock(bridge_channel->chan);
+		ast_bridge_merge_inhibit(bridge, -1);
+		ao2_ref(bridge, -1);
 		return 0;
 	}
 	if (attended_transfer) {
@@ -1207,6 +1215,15 @@
 		atxfer_complete = ast_strdupa(xfer_cfg->atxfercomplete);
 	}
 	fail_sound = ast_strdupa(xfer_cfg->xferfailsound);
+	atxferdropcall = xfer_cfg->atxferdropcall;
+	atxfercallbackretries = xfer_cfg->atxfercallbackretries;
+	atxfernoanswertimeout = xfer_cfg->atxfernoanswertimeout;
+	if (add_transferer_role(bridge_channel->chan, atxfer_abort, atxfer_complete, atxfer_threeway)) {
+		ast_channel_unlock(bridge_channel->chan);
+		ast_bridge_merge_inhibit(bridge, -1);
+		ao2_ref(bridge, -1);
+		return 0;
+	}
 	ast_channel_unlock(bridge_channel->chan);
 
 	/* Grab the extension to transfer to */
@@ -1217,8 +1234,11 @@
 		return 0;
 	}
 
+	/* Fill the variable with the extension and context we want to call */
+	snprintf(destination, sizeof(destination), "%s@%s", exten, context);
+
 	/* Get a channel that is the destination we wish to call */
-	peer = dial_transfer(bridge_channel->chan, exten, context);
+	peer = dial_transfer(bridge_channel->chan, destination);
 	if (!peer) {
 		ast_bridge_merge_inhibit(bridge, -1);
 		ao2_ref(bridge, -1);
@@ -1227,29 +1247,8 @@
 		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);
+	attended_bridge = ast_bridge_basic_new();
 	if (!attended_bridge) {
 		ast_bridge_features_cleanup(&caller_features);
 		ast_hangup(peer);
@@ -1261,9 +1260,10 @@
 	}
 	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)) {
+	props = attended_transfer_properties_alloc(bridge, attended_bridge,
+			bridge_channel->chan, peer, atxferdropcall, atxfernoanswertimeout, atxfercallbackretries);
+	
+	if (!props) {
 		ast_bridge_destroy(attended_bridge);
 		ast_bridge_features_cleanup(&caller_features);
 		ast_hangup(peer);
@@ -1274,13 +1274,42 @@
 		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_basic_change_personality_atxfer(attended_bridge, props);
+
+	if (ast_call(peer, destination, 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);
+		ao2_cleanup(props);
+		ast_bridge_channel_write_unhold(bridge_channel);
+		return 0;
+	}
+
+	/* This is how this is going down, we are imparting the channel we called above into this bridge first */
+	if (ast_bridge_impart(attended_bridge, peer, NULL, NULL, 1)) {
+		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);
+		ao2_cleanup(props);
+		ast_bridge_channel_write_unhold(bridge_channel);
+		return 0;
+	}
+
+	ast_pthread_create_detached(&thread, NULL, attended_transfer_monitor_thread, props);
+
+	/* Once the monitoring thread has been created, it is responsible for destroying all
+	 * of the necessary components.
 	 */
-	ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features, NULL, 0);
-
+	return 0;
+	
+	/* XXX Leaving the rest of the original commented out as a reference for the time being */
+#if 0
 /*
  * BUGBUG there is a small window where the channel does not point to the bridge_channel.
  *
@@ -1358,6 +1387,7 @@
 	}
 
 	return 0;
+#endif
 }
 
 struct ast_bridge_methods ast_bridge_basic_v_table;




More information about the svn-commits mailing list