[svn-commits] file: branch file/bridging r173062 - in /team/file/bridging: apps/ bridges/ i...
SVN commits to the Digium repositories
svn-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 svn-commits
mailing list