[asterisk-commits] file: branch file/bridging r173062 - in /team/file/bridging: apps/ bridges/ i...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Feb 2 17:25:41 CST 2009


Author: file
Date: Mon Feb  2 17:25:40 2009
New Revision: 173062

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=173062
Log:
Take care of some more of Mark's comments. This commit includes a new API call to see whether it is possible for the bridging core to produce a bridge given certain capabilities. Some doxygen documentation was not totally filled in, this has been fixed. The big change though is that the built in features are now a separate module which is optional to operation. Additionally the built in features themselves can be passed in configuration parameters using a structure.

Added:
    team/file/bridging/bridges/bridge_builtin_features.c   (with props)
Modified:
    team/file/bridging/apps/app_confbridge.c
    team/file/bridging/include/asterisk/bridging.h
    team/file/bridging/include/asterisk/bridging_features.h
    team/file/bridging/include/asterisk/bridging_technology.h
    team/file/bridging/main/bridging.c

Modified: team/file/bridging/apps/app_confbridge.c
URL: http://svn.digium.com/svn-view/asterisk/team/file/bridging/apps/app_confbridge.c?view=diff&rev=173062&r1=173061&r2=173062
==============================================================================
--- team/file/bridging/apps/app_confbridge.c (original)
+++ team/file/bridging/apps/app_confbridge.c Mon Feb  2 17:25:40 2009
@@ -421,6 +421,13 @@
 
 	/* If no conference bridge was found see if we can create one */
 	if (!conference_bridge) {
+		/* See if it is even possible for us to create a bridge that will go to a multimix if needed */
+		if (!ast_bridge_check(AST_BRIDGE_CAPABILITY_MULTIMIX)) {
+			ao2_unlock(conference_bridges);
+			ast_log(LOG_ERROR, "Conference bridge '%s' could not be created. Bridging core is incapable of going to multi party.\n", name);
+			return NULL;
+		}
+		
 		/* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
 		if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
 			ao2_unlock(conference_bridges);
@@ -715,7 +722,7 @@
 	if (args.argc == 2) {
 		ast_app_parse_options(app_opts, &conference_bridge_user.flags, conference_bridge_user.opt_args, args.options);
 	}
-
+	
 	/* Look for a conference bridge matching the provided name */
 	if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
 		return -1;

Added: team/file/bridging/bridges/bridge_builtin_features.c
URL: http://svn.digium.com/svn-view/asterisk/team/file/bridging/bridges/bridge_builtin_features.c?view=auto&rev=173062
==============================================================================
--- team/file/bridging/bridges/bridge_builtin_features.c (added)
+++ team/file/bridging/bridges/bridge_builtin_features.c Mon Feb  2 17:25:40 2009
@@ -1,0 +1,257 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009, Digium, Inc.
+ *
+ * Joshua Colp <jcolp at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Built in bridging features
+ *
+ * \author Joshua Colp <jcolp at digium.com>
+ *
+ * \ingroup bridges
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/bridging.h"
+#include "asterisk/bridging_technology.h"
+#include "asterisk/frame.h"
+#include "asterisk/file.h"
+#include "asterisk/app.h"
+#include "asterisk/astobj2.h"
+
+/*! \brief Helper function that presents dialtone and grabs extension */
+static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len, const char *context)
+{
+	int res;
+
+	/* Play the simple "transfer" prompt out and wait */
+	res = ast_stream_and_wait(chan, "pbx-transfer", AST_DIGIT_ANY);
+	ast_stopstream(chan);
+
+	/* If the person hit a DTMF digit while the above played back stick it into the buffer */
+	if (res) {
+		exten[0] = (char)res;
+	}
+
+	/* Drop to dialtone so they can enter the extension they want to transfer to */
+	res = ast_app_dtget(chan, context, exten, exten_len, 100, 1000);
+
+	return res;
+}
+
+/*! \brief Helper function that creates an outgoing channel and returns it immediately */
+static struct ast_channel *dial_transfer(const struct ast_channel *caller, const char *exten, const char *context)
+{
+	char destination[AST_MAX_EXTENSION+AST_MAX_CONTEXT+1] = "";
+	struct ast_channel *chan = NULL;
+	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 that chan_local prepare to call the destination */
+	if (!(chan = ast_request("Local", caller->nativeformats, destination, &cause))) {
+		return NULL;
+	}
+
+	/* Before we actually dial out let's inherit the appropriate dialplan variables */
+	ast_channel_inherit_variables(caller, chan);
+
+	/* 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;
+}
+
+/*! \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 = (blind_transfer && !ast_strlen_zero(blind_transfer->context) ? blind_transfer->context : bridge_channel->chan->context);
+	
+	/* Grab the extension to transfer to */
+	if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
+		ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY);
+		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
+		return 0;
+	}
+
+	/* Get a channel that is the destination we wish to call */
+	if (!(chan = dial_transfer(bridge_channel->chan, exten, context))) {
+		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
+		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
+		return 0;
+	}
+
+	/* This is sort of the fun part. We impart the above channel onto the bridge, and have it take our place. */
+	ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL);
+
+	return 0;
+}
+
+/*! \brief Attended transfer feature to turn it into a threeway call */
+static int attended_threeway_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	/* This is sort of abusing the depart state but in this instance it is only going to be handled in the below function so it is okay */
+	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_DEPART);
+	return 0;
+}
+
+/*! \brief Attended transfer abort feature */
+static int attended_abort_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	struct ast_bridge_channel *called_bridge_channel = NULL;
+
+	/* It is possible (albeit unlikely) that the bridge channels list may change, so we have to ensure we do all of our magic while locked */
+	ao2_lock(bridge);
+
+	if (AST_LIST_FIRST(&bridge->channels) != bridge_channel) {
+		called_bridge_channel = AST_LIST_FIRST(&bridge->channels);
+	} else {
+		called_bridge_channel = AST_LIST_LAST(&bridge->channels);
+	}
+
+	/* Now we basically eject the other channel from the bridge. This will cause their thread to hang them up, and our own code to consider the transfer failed. */
+	if (called_bridge_channel) {
+		ast_bridge_change_state(called_bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+	}
+	
+	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+
+	ao2_unlock(bridge);
+	
+	return 0;
+}
+
+/*! \brief Internal built in feature for attended transfers */
+static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	char exten[AST_MAX_EXTENSION] = "";
+	struct ast_channel *chan = NULL;
+	struct ast_bridge *attended_bridge = NULL;
+	struct ast_bridge_features caller_features, called_features;
+	enum ast_bridge_channel_state attended_bridge_result;
+	struct ast_bridge_features_attended_transfer *attended_transfer = hook_pvt;
+	const char *context = (attended_transfer && !ast_strlen_zero(attended_transfer->context) ? attended_transfer->context : bridge_channel->chan->context);
+	
+	/* Grab the extension to transfer to */
+	if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
+		ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY);
+		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
+		return 0;
+	}
+
+	/* Get a channel that is the destination we wish to call */
+	if (!(chan = dial_transfer(bridge_channel->chan, exten, context))) {
+		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
+		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
+		return 0;
+	}
+
+	/* Create a bridge to use to talk to the person we are calling */
+	if (!(attended_bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, 0))) {
+		ast_hangup(chan);
+		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
+		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
+		return 0;
+	}
+
+	/* Setup our called features structure so that if they hang up we immediately get thrown out of the bridge */
+	ast_bridge_features_init(&called_features);
+	ast_bridge_features_set_flag(&called_features, AST_BRIDGE_FLAG_DISSOLVE);
+
+	/* This is how this is going down, we are imparting the channel we called above into this bridge first */
+	ast_bridge_impart(attended_bridge, chan, NULL, &called_features);
+
+	/* Before we join setup a features structure with the hangup option, just in case they want to use DTMF */
+	ast_bridge_features_init(&caller_features);
+	ast_bridge_features_enable(&caller_features, AST_BRIDGE_BUILTIN_HANGUP,
+				   (attended_transfer && !ast_strlen_zero(attended_transfer->complete) ? attended_transfer->complete : "*1"), NULL);
+	ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->threeway) ? attended_transfer->threeway : "*2"),
+				 attended_threeway_transfer, NULL);
+	ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->abort) ? attended_transfer->abort : "*3"),
+				 attended_abort_transfer, NULL);
+
+	/* But 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 */
+	attended_bridge_result = ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features);
+
+	/* Since the above returned the caller features structure is of no more use */
+	ast_bridge_features_cleanup(&caller_features);
+
+	/* Drop the channel we are transferring to out of the above bridge since it has ended */
+	if ((attended_bridge_result != AST_BRIDGE_CHANNEL_STATE_HANGUP) && !ast_bridge_depart(attended_bridge, chan)) {
+		/* If the user wants to turn this into a threeway transfer then do so, otherwise they take our place */
+		if (attended_bridge_result == AST_BRIDGE_CHANNEL_STATE_DEPART) {
+			/* We want to impart them upon the bridge and just have us return to it as normal */
+			ast_bridge_impart(bridge, chan, NULL, NULL);
+		} else {
+			ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL);
+		}
+	} else {
+		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
+		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
+	}
+
+	/* Now that all channels are out of it we can destroy the bridge and the called features structure */
+	ast_bridge_features_cleanup(&called_features);
+	ast_bridge_destroy(attended_bridge);
+
+	return 0;
+}
+
+/*! \brief Internal built in feature for hangup */
+static int feature_hangup(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	/* This is very simple, we basically change the state on the bridge channel to end and the core takes care of the rest */
+	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+	return 0;
+}
+		
+static int unload_module(void)
+{
+	return 0;
+}
+
+static int load_module(void)
+{
+	ast_bridge_features_register(AST_BRIDGE_BUILTIN_BLINDTRANSFER, feature_blind_transfer, NULL);
+	ast_bridge_features_register(AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER, feature_attended_transfer, NULL);
+	ast_bridge_features_register(AST_BRIDGE_BUILTIN_HANGUP, feature_hangup, NULL);
+	
+	/* Bump up our reference count so we can't be unloaded */
+	ast_module_ref(ast_module_info->self);
+	
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Built in bridging features");

Propchange: team/file/bridging/bridges/bridge_builtin_features.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/file/bridging/bridges/bridge_builtin_features.c
------------------------------------------------------------------------------
    svn:keywords = 'Author Date Id Revision'

Propchange: team/file/bridging/bridges/bridge_builtin_features.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/file/bridging/include/asterisk/bridging.h
URL: http://svn.digium.com/svn-view/asterisk/team/file/bridging/include/asterisk/bridging.h?view=diff&rev=173062&r1=173061&r2=173062
==============================================================================
--- team/file/bridging/include/asterisk/bridging.h (original)
+++ team/file/bridging/include/asterisk/bridging.h Mon Feb  2 17:25:40 2009
@@ -81,7 +81,7 @@
 	/*! Bridge technology can optimize things based on who is talking */
 	AST_BRIDGE_CAPABILITY_OPTIMIZE = (1 << 7),
 };
-
+	
 /*! \brief State information about a bridged channel */
 enum ast_bridge_channel_state {
 	/*! Waiting for a signal */
@@ -195,6 +195,24 @@
  */
 struct ast_bridge *ast_bridge_new(int capabilities, int flags);
 
+/*! \brief See if it is possible to create a bridge
+ *
+ * \param capabilities The capabilities that the bridge will use
+ *
+ * \retval 1 if possible
+ * \retval 0 if not possible
+ *
+ * Example usage:
+ *
+ * \code
+ * int possible = ast_bridge_check(AST_BRIDGE_CAPABILITY_1TO1MIX);
+ * \endcode
+ *
+ * This sees if it is possible to create a bridge capable of bridging two channels
+ * together.
+ */
+int ast_bridge_check(int capabilities);	
+	
 /*! \brief Destroy a bridge
  *
  * \param bridge Bridge to destroy
@@ -239,7 +257,7 @@
  * can be specified in the features parameter.
  */
 enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features);
-
+	
 /*! \brief Impart (non-blocking) a channel on a bridge
  *
  * \param bridge Bridge to impart on

Modified: team/file/bridging/include/asterisk/bridging_features.h
URL: http://svn.digium.com/svn-view/asterisk/team/file/bridging/include/asterisk/bridging_features.h?view=diff&rev=173062&r1=173061&r2=173062
==============================================================================
--- team/file/bridging/include/asterisk/bridging_features.h (original)
+++ team/file/bridging/include/asterisk/bridging_features.h Mon Feb  2 17:25:40 2009
@@ -54,9 +54,9 @@
 /*!
  * \brief Features hook callback type
  *
- * \param bridge
- * \param bridge_channel
- * \param hook_pvt
+ * \param bridge The bridge that the channel is part of
+ * \param bridge_channel Channel executing the feature
+ * \param hook_pvt Private data passed in when the hook was created
  *
  * \retval 0 success
  * \retval -1 failure
@@ -96,6 +96,65 @@
 	unsigned int mute:1;
 };
 
+/*!
+ * \brief Structure that contains configuration information for the blind transfer built in feature
+ */
+struct ast_bridge_features_blind_transfer {
+	/*! Context to use for transfers */
+	char context[AST_MAX_CONTEXT];
+};
+	
+/*!
+ * \brief Structure that contains configuration information for the attended transfer built in feature
+ */
+struct ast_bridge_features_attended_transfer {
+	/*! DTMF string used to abort the transfer */
+	char abort[MAXIMUM_DTMF_FEATURE_STRING];
+	/*! DTMF string used to turn the transfer into a three way conference */
+	char threeway[MAXIMUM_DTMF_FEATURE_STRING];
+	/*! DTMF string used to complete the transfer */
+	char complete[MAXIMUM_DTMF_FEATURE_STRING];
+	/*! Context to use for transfers */
+	char context[AST_MAX_CONTEXT];
+};
+	
+/*! \brief Register a handler for a built in feature
+ *
+ * \param feature The feature that the handler will be responsible for
+ * \param callback The callback function that will handle it
+ * \param dtmf Default DTMF string used to activate the feature
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_features_register(AST_BRIDGE_BUILTIN_ATTENDED_TRANSFER, bridge_builtin_attended_transfer, "*1");
+ * \endcode
+ *
+ * This registers the function bridge_builtin_attended_transfer as the function responsible for the built in
+ * attended transfer feature.
+ */
+int ast_bridge_features_register(enum ast_bridge_builtin_feature feature, ast_bridge_features_hook_callback callback, const char *dtmf);
+
+/*! \brief Unregister a handler for a built in feature
+ *
+ * \param feature The feature to unregister
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_ATTENDED_TRANSFER);
+ * \endcode
+ *
+ * This unregisters the function that is handling the built in attended transfer feature.
+ */
+int ast_bridge_features_unregister(enum ast_bridge_builtin_feature feature);
+	
 /*! \brief Attach a custom hook to a bridge features structure
  *
  * \param features Bridge features structure
@@ -128,6 +187,7 @@
  * \param features Bridge features structure
  * \param feature Feature to enable
  * \param dtmf Optionally the DTMF stream to trigger the feature, if not specified it will be the default
+ * \param config Configuration structure unique to the built in type
  *
  * \retval 0 on success
  * \retval -1 on failure
@@ -144,7 +204,7 @@
  * string may be provided using the dtmf parameter. Internally this is simply setting up a hook
  * to a built in feature callback function.
  */
-int ast_bridge_features_enable(struct ast_bridge_features *features, enum ast_bridge_builtin_feature feature, const char *dtmf);
+int ast_bridge_features_enable(struct ast_bridge_features *features, enum ast_bridge_builtin_feature feature, const char *dtmf, void *config);
 
 /*! \brief Set a flag on a bridge features structure
  *

Modified: team/file/bridging/include/asterisk/bridging_technology.h
URL: http://svn.digium.com/svn-view/asterisk/team/file/bridging/include/asterisk/bridging_technology.h?view=diff&rev=173062&r1=173061&r2=173062
==============================================================================
--- team/file/bridging/include/asterisk/bridging_technology.h (original)
+++ team/file/bridging/include/asterisk/bridging_technology.h Mon Feb  2 17:25:40 2009
@@ -92,9 +92,10 @@
  * simple_bridge_tech with the bridging core and makes it available for
  * use when creating bridges.
  */
+int __ast_bridge_technology_register(struct ast_bridge_technology *technology, struct ast_module *mod);
+
+/*! \brief See \ref __ast_bridge_technology_register() */	
 #define ast_bridge_technology_register(technology) __ast_bridge_technology_register(technology, ast_module_info->self)
-
-int __ast_bridge_technology_register(struct ast_bridge_technology *technology, struct ast_module *mod);
 
 /*! \brief Unregister a bridge technology from use
  *
@@ -117,10 +118,10 @@
 
 /*! \brief Feed notification that a frame is waiting on a channel into the bridging core
  *
- * \param bridge
- * \param bridge_channel
- * \param chan
- * \param outfd
+ * \param bridge The bridge that the notification should influence
+ * \param bridge_channel Bridge channel the notification was received on (if known)
+ * \param chan Channel the notification was received on (if known)
+ * \param outfd File descriptor that the notification was received on (if known)
  *
  * Example usage:
  *

Modified: team/file/bridging/main/bridging.c
URL: http://svn.digium.com/svn-view/asterisk/team/file/bridging/main/bridging.c?view=diff&rev=173062&r1=173061&r2=173062
==============================================================================
--- team/file/bridging/main/bridging.c (original)
+++ team/file/bridging/main/bridging.c Mon Feb  2 17:25:40 2009
@@ -53,6 +53,9 @@
 /*! Default DTMF keys for built in features */
 static char builtin_features_dtmf[AST_BRIDGE_BUILTIN_END][MAXIMUM_DTMF_FEATURE_STRING];
 
+/*! Function handlers for the built in features */
+static void *builtin_features_handlers[AST_BRIDGE_BUILTIN_END];
+
 int __ast_bridge_technology_register(struct ast_bridge_technology *technology, struct ast_module *module)
 {
 	struct ast_bridge_technology *current = NULL;
@@ -466,6 +469,19 @@
 	return bridge;
 }
 
+int ast_bridge_check(int capabilities)
+{
+	struct ast_bridge_technology *bridge_technology = NULL;
+
+	if (!(bridge_technology = find_best_technology(capabilities))) {
+		return 0;
+	}
+
+	ast_module_unref(bridge_technology->mod);
+	
+	return 1;
+}
+
 int ast_bridge_destroy(struct ast_bridge *bridge)
 {
 	struct ast_bridge_channel *bridge_channel = NULL;
@@ -1163,6 +1179,29 @@
 	return;
 }
 
+int ast_bridge_features_register(enum ast_bridge_builtin_feature feature, ast_bridge_features_hook_callback callback, const char *dtmf)
+{
+	if (builtin_features_handlers[feature]) {
+		return -1;
+	}
+
+	ast_copy_string(builtin_features_dtmf[feature], dtmf, sizeof(builtin_features_dtmf[feature]));
+	builtin_features_handlers[feature] = callback;
+	
+	return 0;
+}
+
+int ast_bridge_features_unregister(enum ast_bridge_builtin_feature feature)
+{
+	if (!builtin_features_handlers[feature]) {
+		return -1;
+	}
+
+	builtin_features_handlers[feature] = NULL;
+	
+	return 0;
+}
+
 int ast_bridge_features_hook(struct ast_bridge_features *features, const char *dtmf, ast_bridge_features_hook_callback callback, void *hook_pvt)
 {
 	struct ast_bridge_features_hook *hook = NULL;
@@ -1184,189 +1223,8 @@
 	return 0;
 }
 
-/*! \brief Helper function that presents dialtone and grabs extension */
-static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len, char *context)
-{
-	int res;
-
-	/* Play the simple "transfer" prompt out and wait */
-	res = ast_stream_and_wait(chan, "pbx-transfer", AST_DIGIT_ANY);
-	ast_stopstream(chan);
-
-	/* If the person hit a DTMF digit while the above played back stick it into the buffer */
-	if (res) {
-		exten[0] = (char)res;
-	}
-
-	/* Drop to dialtone so they can enter the extension they want to transfer to */
-	res = ast_app_dtget(chan, context, exten, exten_len, 100, 1000);
-
-	return res;
-}
-
-/*! \brief Helper function that creates an outgoing channel and returns it immediately */
-static struct ast_channel *dial_transfer(const struct ast_channel *caller, const char *exten, const char *context)
-{
-	char destination[AST_MAX_EXTENSION+AST_MAX_CONTEXT+1] = "";
-	struct ast_channel *chan = NULL;
-	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 that chan_local prepare to call the destination */
-	if (!(chan = ast_request("Local", caller->nativeformats, destination, &cause))) {
-		return NULL;
-	}
-
-	/* Before we actually dial out let's inherit the appropriate dialplan variables */
-	ast_channel_inherit_variables(caller, chan);
-
-	/* 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;
-}
-
-/*! \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;
-
-	/* Grab the extension to transfer to */
-	if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), bridge_channel->chan->context)) {
-		ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY);
-		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
-		return 0;
-	}
-
-	/* Get a channel that is the destination we wish to call */
-	if (!(chan = dial_transfer(bridge_channel->chan, exten, bridge_channel->chan->context))) {
-		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
-		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
-		return 0;
-	}
-
-	/* This is sort of the fun part. We impart the above channel onto the bridge, and have it take our place. */
-	ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL);
-
-	return 0;
-}
-
-/*! \brief Attended transfer feature to turn it into a threeway call */
-static int attended_threeway_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
-	/* This is sort of abusing the depart state but in this instance it is only going to be handled in the below function so it is okay */
-	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_DEPART);
-	return 0;
-}
-
-/*! \brief Attended transfer abort feature */
-static int attended_abort_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
-	struct ast_bridge_channel *called_bridge_channel = NULL;
-
-	/* It is possible (albeit unlikely) that the bridge channels list may change, so we have to ensure we do all of our magic while locked */
-	if (!(called_bridge_channel = (AST_LIST_FIRST(&bridge->channels) != bridge_channel ? AST_LIST_FIRST(&bridge->channels) : AST_LIST_LAST(&bridge->channels)))) {
-		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
-		return 0;
-	}
-
-	/* Now we basically eject the other channel from the bridge. This will cause their thread to hang them up, and our own code to consider the transfer failed. */
-	ast_bridge_change_state(called_bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
-
-	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
-
-	return 0;
-}
-
-/*! \brief Internal built in feature for attended transfers */
-static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
-	char exten[AST_MAX_EXTENSION] = "";
-	struct ast_channel *chan = NULL;
-	struct ast_bridge *attended_bridge = NULL;
-	struct ast_bridge_features caller_features, called_features;
-	enum ast_bridge_channel_state attended_bridge_result;
-
-	/* Grab the extension to transfer to */
-	if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), bridge_channel->chan->context)) {
-		ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY);
-		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
-		return 0;
-	}
-
-	/* Get a channel that is the destination we wish to call */
-	if (!(chan = dial_transfer(bridge_channel->chan, exten, bridge_channel->chan->context))) {
-		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
-		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
-		return 0;
-	}
-
-	/* Create a bridge to use to talk to the person we are calling */
-	if (!(attended_bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, 0))) {
-		ast_hangup(chan);
-		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
-		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
-		return 0;
-	}
-
-	/* Setup our called features structure so that if they hang up we immediately get thrown out of the bridge */
-	ast_bridge_features_init(&called_features);
-	ast_bridge_features_set_flag(&called_features, AST_BRIDGE_FLAG_DISSOLVE);
-
-	/* This is how this is going down, we are imparting the channel we called above into this bridge first */
-	ast_bridge_impart(attended_bridge, chan, NULL, &called_features);
-
-	/* Before we join setup a features structure with the hangup option, just in case they want to use DTMF */
-	ast_bridge_features_init(&caller_features);
-	ast_bridge_features_enable(&caller_features, AST_BRIDGE_BUILTIN_HANGUP, "*1");
-	ast_bridge_features_hook(&caller_features, "*2", attended_threeway_transfer, NULL);
-	ast_bridge_features_hook(&caller_features, "*3", attended_abort_transfer, NULL);
-
-	/* But 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 */
-	attended_bridge_result = ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features);
-
-	/* Since the above returned the caller features structure is of no more use */
-	ast_bridge_features_cleanup(&caller_features);
-
-	/* Drop the channel we are transferring to out of the above bridge since it has ended */
-	if ((attended_bridge_result != AST_BRIDGE_CHANNEL_STATE_HANGUP) && !ast_bridge_depart(attended_bridge, chan)) {
-		/* If the user wants to turn this into a threeway transfer then do so, otherwise they take our place */
-		if (attended_bridge_result == AST_BRIDGE_CHANNEL_STATE_DEPART) {
-			/* We want to impart them upon the bridge and just have us return to it as normal */
-			ast_bridge_impart(bridge, chan, NULL, NULL);
-		} else {
-			ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL);
-		}
-	} else {
-		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
-		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
-	}
-
-	/* Now that all channels are out of it we can destroy the bridge and the called features structure */
-	ast_bridge_features_cleanup(&called_features);
-	ast_bridge_destroy(attended_bridge);
-
-	return 0;
-}
-
-/*! \brief Internal built in feature for hangup */
-static int feature_hangup(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
-	/* This is very simple, we basically change the state on the bridge channel to end and the core takes care of the rest */
-	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
-	return 0;
-}
-
-int ast_bridge_features_enable(struct ast_bridge_features *features, enum ast_bridge_builtin_feature feature, const char *dtmf)
-{
-	ast_bridge_features_hook_callback callback = NULL;
-
+int ast_bridge_features_enable(struct ast_bridge_features *features, enum ast_bridge_builtin_feature feature, const char *dtmf, void *config)
+{
 	/* If no alternate DTMF stream was provided use the default one */
 	if (ast_strlen_zero(dtmf)) {
 		dtmf = builtin_features_dtmf[feature];
@@ -1377,19 +1235,12 @@
 		}
 	}
 
-	/* Grab the callback for each feature */
-	if (feature == AST_BRIDGE_BUILTIN_BLINDTRANSFER) {
-		callback = feature_blind_transfer;
-	} else if (feature == AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER) {
-		callback = feature_attended_transfer;
-	} else if (feature == AST_BRIDGE_BUILTIN_HANGUP) {
-		callback = feature_hangup;
-	} else {
+	if (!builtin_features_handlers[feature]) {
 		return -1;
 	}
 
 	/* The rest is basically pretty easy. We create another hook using the built in feature's callback and DTMF, easy as pie. */
-	return ast_bridge_features_hook(features, dtmf, callback, NULL);
+	return ast_bridge_features_hook(features, dtmf, builtin_features_handlers[feature], config);
 }
 
 int ast_bridge_features_set_flag(struct ast_bridge_features *features, enum ast_bridge_feature_flags flag)




More information about the asterisk-commits mailing list