mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-03 03:20:57 +00:00
* Move ast_bridge_channel_setup_features() into bridge_basic.c.
* Made application map hooks be removed on a basic bridge personality change. git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@397355 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -104,17 +104,6 @@ int ast_bridge_features_ds_set(struct ast_channel *chan, struct ast_flags *flags
|
||||
*/
|
||||
int ast_bridge_features_ds_append(struct ast_channel *chan, struct ast_flags *flags);
|
||||
|
||||
/*!
|
||||
* \brief Setup DTMF feature hooks using the channel features datastore property.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param bridge_channel What to setup DTMF features on.
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on error.
|
||||
*/
|
||||
int ast_bridge_channel_setup_features(struct ast_bridge_channel *bridge_channel);
|
||||
|
||||
/*! \brief Bridge basic class virtual method table. */
|
||||
extern struct ast_bridge_methods ast_bridge_basic_v_table;
|
||||
|
||||
|
@@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/dial.h"
|
||||
#include "asterisk/stasis_bridges.h"
|
||||
#include "asterisk/features.h"
|
||||
|
||||
#define NORMAL_FLAGS (AST_BRIDGE_FLAG_DISSOLVE_HANGUP | AST_BRIDGE_FLAG_DISSOLVE_EMPTY \
|
||||
| AST_BRIDGE_FLAG_SMART)
|
||||
@@ -329,11 +330,319 @@ struct bridge_basic_personality {
|
||||
struct personality_details details[BRIDGE_BASIC_PERSONALITY_END];
|
||||
};
|
||||
|
||||
/*
|
||||
* \internal
|
||||
* \brief Get the extension for a given builtin feature.
|
||||
*
|
||||
* \param chan Get the feature extension for this channel.
|
||||
* \param feature_name features.conf name of feature.
|
||||
* \param buf Where to put the extension.
|
||||
* \param len Length of the given extension buffer.
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval non-zero failiure
|
||||
*/
|
||||
static int builtin_feature_get_exten(struct ast_channel *chan, const char *feature_name, char *buf, size_t len)
|
||||
{
|
||||
SCOPED_CHANNELLOCK(lock, chan);
|
||||
|
||||
return ast_get_builtin_feature(chan, feature_name, buf, len);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Helper to add a builtin DTMF feature hook to the features struct.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param features Bridge features to setup.
|
||||
* \param chan Get features from this channel.
|
||||
* \param flags Feature flags on the channel.
|
||||
* \param feature_flag Feature flag to test.
|
||||
* \param feature_name features.conf name of feature.
|
||||
* \param feature_bridge Bridge feature enum to get hook callback.
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on error.
|
||||
*/
|
||||
static int builtin_features_helper(struct ast_bridge_features *features, struct ast_channel *chan,
|
||||
struct ast_flags *flags, unsigned int feature_flag, const char *feature_name, enum ast_bridge_builtin_feature feature_bridge)
|
||||
{
|
||||
char dtmf[AST_FEATURE_MAX_LEN];
|
||||
int res;
|
||||
|
||||
res = 0;
|
||||
if (ast_test_flag(flags, feature_flag)
|
||||
&& !builtin_feature_get_exten(chan, feature_name, dtmf, sizeof(dtmf))
|
||||
&& !ast_strlen_zero(dtmf)) {
|
||||
res = ast_bridge_features_enable(features, feature_bridge, dtmf, NULL, NULL,
|
||||
AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
|
||||
if (res) {
|
||||
ast_log(LOG_ERROR, "Channel %s: Requested DTMF feature %s not available.\n",
|
||||
ast_channel_name(chan), feature_name);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Setup bridge builtin features.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param features Bridge features to setup.
|
||||
* \param chan Get features from this channel.
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on error.
|
||||
*/
|
||||
static int setup_bridge_features_builtin(struct ast_bridge_features *features, struct ast_channel *chan)
|
||||
{
|
||||
struct ast_flags *flags;
|
||||
int res;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
flags = ast_bridge_features_ds_get(chan);
|
||||
ast_channel_unlock(chan);
|
||||
if (!flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
res = 0;
|
||||
res |= builtin_features_helper(features, chan, flags, AST_FEATURE_REDIRECT, "blindxfer", AST_BRIDGE_BUILTIN_BLINDTRANSFER);
|
||||
res |= builtin_features_helper(features, chan, flags, AST_FEATURE_REDIRECT, "atxfer", AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER);
|
||||
res |= builtin_features_helper(features, chan, flags, AST_FEATURE_DISCONNECT, "disconnect", AST_BRIDGE_BUILTIN_HANGUP);
|
||||
res |= builtin_features_helper(features, chan, flags, AST_FEATURE_PARKCALL, "parkcall", AST_BRIDGE_BUILTIN_PARKCALL);
|
||||
res |= builtin_features_helper(features, chan, flags, AST_FEATURE_AUTOMON, "automon", AST_BRIDGE_BUILTIN_AUTOMON);
|
||||
res |= builtin_features_helper(features, chan, flags, AST_FEATURE_AUTOMIXMON, "automixmon", AST_BRIDGE_BUILTIN_AUTOMIXMON);
|
||||
|
||||
return res ? -1 : 0;
|
||||
}
|
||||
|
||||
struct dynamic_dtmf_hook_run {
|
||||
/*! Offset into app_name[] where the channel name that activated the hook starts. */
|
||||
int activated_offset;
|
||||
/*! Offset into app_name[] where the dynamic feature name starts. */
|
||||
int feature_offset;
|
||||
/*! Offset into app_name[] where the MOH class name starts. (zero if no MOH) */
|
||||
int moh_offset;
|
||||
/*! Offset into app_name[] where the application argument string starts. (zero if no arguments) */
|
||||
int app_args_offset;
|
||||
/*! Application name to run. */
|
||||
char app_name[0];
|
||||
};
|
||||
|
||||
static void dynamic_dtmf_hook_callback(struct ast_bridge_channel *bridge_channel,
|
||||
const void *payload, size_t payload_size)
|
||||
{
|
||||
struct ast_channel *chan = bridge_channel->chan;
|
||||
const struct dynamic_dtmf_hook_run *run_data = payload;
|
||||
|
||||
pbx_builtin_setvar_helper(chan, "DYNAMIC_FEATURENAME",
|
||||
&run_data->app_name[run_data->feature_offset]);
|
||||
pbx_builtin_setvar_helper(chan, "DYNAMIC_WHO_ACTIVATED",
|
||||
&run_data->app_name[run_data->activated_offset]);
|
||||
|
||||
ast_bridge_channel_run_app(bridge_channel, run_data->app_name,
|
||||
run_data->app_args_offset ? &run_data->app_name[run_data->app_args_offset] : NULL,
|
||||
run_data->moh_offset ? &run_data->app_name[run_data->moh_offset] : NULL);
|
||||
}
|
||||
|
||||
struct dynamic_dtmf_hook_data {
|
||||
/*! Which side of bridge to run app (AST_FEATURE_FLAG_ONSELF/AST_FEATURE_FLAG_ONPEER) */
|
||||
unsigned int flags;
|
||||
/*! Offset into app_name[] where the dynamic feature name starts. */
|
||||
int feature_offset;
|
||||
/*! Offset into app_name[] where the MOH class name starts. (zero if no MOH) */
|
||||
int moh_offset;
|
||||
/*! Offset into app_name[] where the application argument string starts. (zero if no arguments) */
|
||||
int app_args_offset;
|
||||
/*! Application name to run. */
|
||||
char app_name[0];
|
||||
};
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Activated dynamic DTMF feature hook.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param bridge_channel Channel executing the feature
|
||||
* \param hook_pvt Private data passed in when the hook was created
|
||||
*
|
||||
* \retval 0 Keep the callback hook.
|
||||
* \retval -1 Remove the callback hook.
|
||||
*/
|
||||
static int dynamic_dtmf_hook_trip(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
|
||||
{
|
||||
struct dynamic_dtmf_hook_data *pvt = hook_pvt;
|
||||
struct dynamic_dtmf_hook_run *run_data;
|
||||
const char *activated_name;
|
||||
size_t len_name;
|
||||
size_t len_args;
|
||||
size_t len_moh;
|
||||
size_t len_feature;
|
||||
size_t len_activated;
|
||||
size_t len_data;
|
||||
|
||||
/* Determine lengths of things. */
|
||||
len_name = strlen(pvt->app_name) + 1;
|
||||
len_args = pvt->app_args_offset ? strlen(&pvt->app_name[pvt->app_args_offset]) + 1 : 0;
|
||||
len_moh = pvt->moh_offset ? strlen(&pvt->app_name[pvt->moh_offset]) + 1 : 0;
|
||||
len_feature = strlen(&pvt->app_name[pvt->feature_offset]) + 1;
|
||||
ast_channel_lock(bridge_channel->chan);
|
||||
activated_name = ast_strdupa(ast_channel_name(bridge_channel->chan));
|
||||
ast_channel_unlock(bridge_channel->chan);
|
||||
len_activated = strlen(activated_name) + 1;
|
||||
len_data = sizeof(*run_data) + len_name + len_args + len_moh + len_feature + len_activated;
|
||||
|
||||
/* Fill in dynamic feature run hook data. */
|
||||
run_data = ast_alloca(len_data);
|
||||
run_data->app_args_offset = len_args ? len_name : 0;
|
||||
run_data->moh_offset = len_moh ? len_name + len_args : 0;
|
||||
run_data->feature_offset = len_name + len_args + len_moh;
|
||||
run_data->activated_offset = len_name + len_args + len_moh + len_feature;
|
||||
strcpy(run_data->app_name, pvt->app_name);/* Safe */
|
||||
if (len_args) {
|
||||
strcpy(&run_data->app_name[run_data->app_args_offset],
|
||||
&pvt->app_name[pvt->app_args_offset]);/* Safe */
|
||||
}
|
||||
if (len_moh) {
|
||||
strcpy(&run_data->app_name[run_data->moh_offset],
|
||||
&pvt->app_name[pvt->moh_offset]);/* Safe */
|
||||
}
|
||||
strcpy(&run_data->app_name[run_data->feature_offset],
|
||||
&pvt->app_name[pvt->feature_offset]);/* Safe */
|
||||
strcpy(&run_data->app_name[run_data->activated_offset], activated_name);/* Safe */
|
||||
|
||||
if (ast_test_flag(pvt, AST_FEATURE_FLAG_ONPEER)) {
|
||||
ast_bridge_channel_write_callback(bridge_channel,
|
||||
AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA,
|
||||
dynamic_dtmf_hook_callback, run_data, len_data);
|
||||
} else {
|
||||
dynamic_dtmf_hook_callback(bridge_channel, run_data, len_data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Add a dynamic DTMF feature hook to the bridge features.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param features Bridge features to setup.
|
||||
* \param flags Which side of bridge to run app (AST_FEATURE_FLAG_ONSELF/AST_FEATURE_FLAG_ONPEER).
|
||||
* \param dtmf DTMF trigger sequence.
|
||||
* \param feature_name Name of the dynamic feature.
|
||||
* \param app_name Dialplan application name to run.
|
||||
* \param app_args Dialplan application arguments. (Empty or NULL if no arguments)
|
||||
* \param moh_class MOH class to play to peer. (Empty or NULL if no MOH played)
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on error.
|
||||
*/
|
||||
static int dynamic_dtmf_hook_add(struct ast_bridge_features *features, unsigned int flags, const char *dtmf, const char *feature_name, const char *app_name, const char *app_args, const char *moh_class)
|
||||
{
|
||||
struct dynamic_dtmf_hook_data *hook_data;
|
||||
size_t len_name = strlen(app_name) + 1;
|
||||
size_t len_args = ast_strlen_zero(app_args) ? 0 : strlen(app_args) + 1;
|
||||
size_t len_moh = ast_strlen_zero(moh_class) ? 0 : strlen(moh_class) + 1;
|
||||
size_t len_feature = strlen(feature_name) + 1;
|
||||
size_t len_data = sizeof(*hook_data) + len_name + len_args + len_moh + len_feature;
|
||||
int res;
|
||||
|
||||
/* Fill in application run hook data. */
|
||||
hook_data = ast_malloc(len_data);
|
||||
if (!hook_data) {
|
||||
return -1;
|
||||
}
|
||||
hook_data->flags = flags;
|
||||
hook_data->app_args_offset = len_args ? len_name : 0;
|
||||
hook_data->moh_offset = len_moh ? len_name + len_args : 0;
|
||||
hook_data->feature_offset = len_name + len_args + len_moh;
|
||||
strcpy(hook_data->app_name, app_name);/* Safe */
|
||||
if (len_args) {
|
||||
strcpy(&hook_data->app_name[hook_data->app_args_offset], app_args);/* Safe */
|
||||
}
|
||||
if (len_moh) {
|
||||
strcpy(&hook_data->app_name[hook_data->moh_offset], moh_class);/* Safe */
|
||||
}
|
||||
strcpy(&hook_data->app_name[hook_data->feature_offset], feature_name);/* Safe */
|
||||
|
||||
res = ast_bridge_dtmf_hook(features, dtmf, dynamic_dtmf_hook_trip, hook_data,
|
||||
ast_free_ptr,
|
||||
AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
|
||||
if (res) {
|
||||
ast_free(hook_data);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int setup_dynamic_feature(void *obj, void *arg, void *data, int flags)
|
||||
{
|
||||
struct ast_applicationmap_item *item = obj;
|
||||
struct ast_bridge_features *features = arg;
|
||||
int *res = data;
|
||||
|
||||
*res |= dynamic_dtmf_hook_add(features,
|
||||
item->activate_on_self ? AST_FEATURE_FLAG_ONSELF : AST_FEATURE_FLAG_ONPEER,
|
||||
item->dtmf, item->name, item->app, item->app_data, item->moh_class);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Setup bridge dynamic features.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param features Bridge features to setup.
|
||||
* \param chan Get features from this channel.
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on error.
|
||||
*/
|
||||
static int setup_bridge_features_dynamic(struct ast_bridge_features *features, struct ast_channel *chan)
|
||||
{
|
||||
RAII_VAR(struct ao2_container *, applicationmap, NULL, ao2_cleanup);
|
||||
int res = 0;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
applicationmap = ast_get_chan_applicationmap(chan);
|
||||
ast_channel_unlock(chan);
|
||||
if (!applicationmap) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ao2_callback_data(applicationmap, 0, setup_dynamic_feature, features, &res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Setup DTMF feature hooks using the channel features datastore property.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param bridge_channel What to setup DTMF features on.
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on error.
|
||||
*/
|
||||
static int bridge_basic_setup_features(struct ast_bridge_channel *bridge_channel)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
res |= setup_bridge_features_builtin(bridge_channel->features, bridge_channel->chan);
|
||||
res |= setup_bridge_features_dynamic(bridge_channel->features, bridge_channel->chan);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int add_normal_hooks(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
|
||||
{
|
||||
return ast_bridge_hangup_hook(bridge_channel->features, basic_hangup_hook,
|
||||
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL)
|
||||
|| ast_bridge_channel_setup_features(bridge_channel);
|
||||
|| bridge_basic_setup_features(bridge_channel);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
296
main/features.c
296
main/features.c
@@ -323,23 +323,6 @@ static const struct ast_datastore_info channel_app_data_datastore = {
|
||||
.destroy = ast_free_ptr,
|
||||
};
|
||||
|
||||
/*
|
||||
* \internal
|
||||
* \brief Get the extension for a given builtin feature
|
||||
*
|
||||
* \pre expects features_lock to be readlocked
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval non-zero failiure
|
||||
*/
|
||||
static int builtin_feature_get_exten(struct ast_channel *chan, const char *feature_name,
|
||||
char *buf, size_t len)
|
||||
{
|
||||
SCOPED_CHANNELLOCK(lock, chan);
|
||||
|
||||
return ast_get_builtin_feature(chan, feature_name, buf, len);
|
||||
}
|
||||
|
||||
static void set_config_flags(struct ast_channel *chan, struct ast_bridge_config *config)
|
||||
{
|
||||
ast_clear_flag(config, AST_FLAGS_ALL);
|
||||
@@ -459,285 +442,6 @@ static void clear_dialed_interfaces(struct ast_channel *chan)
|
||||
ast_channel_unlock(chan);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Helper to add a builtin DTMF feature hook to the features struct.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param features Bridge features to setup.
|
||||
* \param chan Get features from this channel.
|
||||
* \param flags Feature flags on the channel.
|
||||
* \param feature_flag Feature flag to test.
|
||||
* \param feature_name features.conf name of feature.
|
||||
* \param feature_bridge Bridge feature enum to get hook callback.
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on error.
|
||||
*/
|
||||
static int builtin_features_helper(struct ast_bridge_features *features, struct ast_channel *chan,
|
||||
struct ast_flags *flags, unsigned int feature_flag, const char *feature_name, enum ast_bridge_builtin_feature feature_bridge)
|
||||
{
|
||||
char dtmf[AST_FEATURE_MAX_LEN];
|
||||
int res;
|
||||
|
||||
res = 0;
|
||||
if (ast_test_flag(flags, feature_flag)
|
||||
&& !builtin_feature_get_exten(chan, feature_name, dtmf, sizeof(dtmf))
|
||||
&& !ast_strlen_zero(dtmf)) {
|
||||
res = ast_bridge_features_enable(features, feature_bridge, dtmf, NULL, NULL,
|
||||
AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
|
||||
if (res) {
|
||||
ast_log(LOG_ERROR, "Channel %s: Requested DTMF feature %s not available.\n",
|
||||
ast_channel_name(chan), feature_name);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Setup bridge builtin features.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param features Bridge features to setup.
|
||||
* \param chan Get features from this channel.
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on error.
|
||||
*/
|
||||
static int setup_bridge_features_builtin(struct ast_bridge_features *features, struct ast_channel *chan)
|
||||
{
|
||||
struct ast_flags *flags;
|
||||
int res;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
flags = ast_bridge_features_ds_get(chan);
|
||||
ast_channel_unlock(chan);
|
||||
if (!flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
res = 0;
|
||||
res |= builtin_features_helper(features, chan, flags, AST_FEATURE_REDIRECT, "blindxfer", AST_BRIDGE_BUILTIN_BLINDTRANSFER);
|
||||
res |= builtin_features_helper(features, chan, flags, AST_FEATURE_REDIRECT, "atxfer", AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER);
|
||||
res |= builtin_features_helper(features, chan, flags, AST_FEATURE_DISCONNECT, "disconnect", AST_BRIDGE_BUILTIN_HANGUP);
|
||||
res |= builtin_features_helper(features, chan, flags, AST_FEATURE_PARKCALL, "parkcall", AST_BRIDGE_BUILTIN_PARKCALL);
|
||||
res |= builtin_features_helper(features, chan, flags, AST_FEATURE_AUTOMON, "automon", AST_BRIDGE_BUILTIN_AUTOMON);
|
||||
res |= builtin_features_helper(features, chan, flags, AST_FEATURE_AUTOMIXMON, "automixmon", AST_BRIDGE_BUILTIN_AUTOMIXMON);
|
||||
|
||||
return res ? -1 : 0;
|
||||
}
|
||||
|
||||
struct dynamic_dtmf_hook_run {
|
||||
/*! Offset into app_name[] where the channel name that activated the hook starts. */
|
||||
int activated_offset;
|
||||
/*! Offset into app_name[] where the dynamic feature name starts. */
|
||||
int feature_offset;
|
||||
/*! Offset into app_name[] where the MOH class name starts. (zero if no MOH) */
|
||||
int moh_offset;
|
||||
/*! Offset into app_name[] where the application argument string starts. (zero if no arguments) */
|
||||
int app_args_offset;
|
||||
/*! Application name to run. */
|
||||
char app_name[0];
|
||||
};
|
||||
|
||||
static void dynamic_dtmf_hook_callback(struct ast_bridge_channel *bridge_channel,
|
||||
const void *payload, size_t payload_size)
|
||||
{
|
||||
struct ast_channel *chan = bridge_channel->chan;
|
||||
const struct dynamic_dtmf_hook_run *run_data = payload;
|
||||
|
||||
pbx_builtin_setvar_helper(chan, "DYNAMIC_FEATURENAME",
|
||||
&run_data->app_name[run_data->feature_offset]);
|
||||
pbx_builtin_setvar_helper(chan, "DYNAMIC_WHO_ACTIVATED",
|
||||
&run_data->app_name[run_data->activated_offset]);
|
||||
|
||||
ast_bridge_channel_run_app(bridge_channel, run_data->app_name,
|
||||
run_data->app_args_offset ? &run_data->app_name[run_data->app_args_offset] : NULL,
|
||||
run_data->moh_offset ? &run_data->app_name[run_data->moh_offset] : NULL);
|
||||
}
|
||||
|
||||
struct dynamic_dtmf_hook_data {
|
||||
/*! Which side of bridge to run app (AST_FEATURE_FLAG_ONSELF/AST_FEATURE_FLAG_ONPEER) */
|
||||
unsigned int flags;
|
||||
/*! Offset into app_name[] where the dynamic feature name starts. */
|
||||
int feature_offset;
|
||||
/*! Offset into app_name[] where the MOH class name starts. (zero if no MOH) */
|
||||
int moh_offset;
|
||||
/*! Offset into app_name[] where the application argument string starts. (zero if no arguments) */
|
||||
int app_args_offset;
|
||||
/*! Application name to run. */
|
||||
char app_name[0];
|
||||
};
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Activated dynamic DTMF feature hook.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param bridge_channel Channel executing the feature
|
||||
* \param hook_pvt Private data passed in when the hook was created
|
||||
*
|
||||
* \retval 0 Keep the callback hook.
|
||||
* \retval -1 Remove the callback hook.
|
||||
*/
|
||||
static int dynamic_dtmf_hook_trip(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
|
||||
{
|
||||
struct dynamic_dtmf_hook_data *pvt = hook_pvt;
|
||||
struct dynamic_dtmf_hook_run *run_data;
|
||||
const char *activated_name;
|
||||
size_t len_name;
|
||||
size_t len_args;
|
||||
size_t len_moh;
|
||||
size_t len_feature;
|
||||
size_t len_activated;
|
||||
size_t len_data;
|
||||
|
||||
/* Determine lengths of things. */
|
||||
len_name = strlen(pvt->app_name) + 1;
|
||||
len_args = pvt->app_args_offset ? strlen(&pvt->app_name[pvt->app_args_offset]) + 1 : 0;
|
||||
len_moh = pvt->moh_offset ? strlen(&pvt->app_name[pvt->moh_offset]) + 1 : 0;
|
||||
len_feature = strlen(&pvt->app_name[pvt->feature_offset]) + 1;
|
||||
ast_channel_lock(bridge_channel->chan);
|
||||
activated_name = ast_strdupa(ast_channel_name(bridge_channel->chan));
|
||||
ast_channel_unlock(bridge_channel->chan);
|
||||
len_activated = strlen(activated_name) + 1;
|
||||
len_data = sizeof(*run_data) + len_name + len_args + len_moh + len_feature + len_activated;
|
||||
|
||||
/* Fill in dynamic feature run hook data. */
|
||||
run_data = ast_alloca(len_data);
|
||||
run_data->app_args_offset = len_args ? len_name : 0;
|
||||
run_data->moh_offset = len_moh ? len_name + len_args : 0;
|
||||
run_data->feature_offset = len_name + len_args + len_moh;
|
||||
run_data->activated_offset = len_name + len_args + len_moh + len_feature;
|
||||
strcpy(run_data->app_name, pvt->app_name);/* Safe */
|
||||
if (len_args) {
|
||||
strcpy(&run_data->app_name[run_data->app_args_offset],
|
||||
&pvt->app_name[pvt->app_args_offset]);/* Safe */
|
||||
}
|
||||
if (len_moh) {
|
||||
strcpy(&run_data->app_name[run_data->moh_offset],
|
||||
&pvt->app_name[pvt->moh_offset]);/* Safe */
|
||||
}
|
||||
strcpy(&run_data->app_name[run_data->feature_offset],
|
||||
&pvt->app_name[pvt->feature_offset]);/* Safe */
|
||||
strcpy(&run_data->app_name[run_data->activated_offset], activated_name);/* Safe */
|
||||
|
||||
if (ast_test_flag(pvt, AST_FEATURE_FLAG_ONPEER)) {
|
||||
ast_bridge_channel_write_callback(bridge_channel,
|
||||
AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA,
|
||||
dynamic_dtmf_hook_callback, run_data, len_data);
|
||||
} else {
|
||||
dynamic_dtmf_hook_callback(bridge_channel, run_data, len_data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Add a dynamic DTMF feature hook to the bridge features.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param features Bridge features to setup.
|
||||
* \param flags Which side of bridge to run app (AST_FEATURE_FLAG_ONSELF/AST_FEATURE_FLAG_ONPEER).
|
||||
* \param dtmf DTMF trigger sequence.
|
||||
* \param feature_name Name of the dynamic feature.
|
||||
* \param app_name Dialplan application name to run.
|
||||
* \param app_args Dialplan application arguments. (Empty or NULL if no arguments)
|
||||
* \param moh_class MOH class to play to peer. (Empty or NULL if no MOH played)
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on error.
|
||||
*/
|
||||
static int dynamic_dtmf_hook_add(struct ast_bridge_features *features, unsigned int flags, const char *dtmf, const char *feature_name, const char *app_name, const char *app_args, const char *moh_class)
|
||||
{
|
||||
struct dynamic_dtmf_hook_data *hook_data;
|
||||
size_t len_name = strlen(app_name) + 1;
|
||||
size_t len_args = ast_strlen_zero(app_args) ? 0 : strlen(app_args) + 1;
|
||||
size_t len_moh = ast_strlen_zero(moh_class) ? 0 : strlen(moh_class) + 1;
|
||||
size_t len_feature = strlen(feature_name) + 1;
|
||||
size_t len_data = sizeof(*hook_data) + len_name + len_args + len_moh + len_feature;
|
||||
int res;
|
||||
|
||||
/* Fill in application run hook data. */
|
||||
hook_data = ast_malloc(len_data);
|
||||
if (!hook_data) {
|
||||
return -1;
|
||||
}
|
||||
hook_data->flags = flags;
|
||||
hook_data->app_args_offset = len_args ? len_name : 0;
|
||||
hook_data->moh_offset = len_moh ? len_name + len_args : 0;
|
||||
hook_data->feature_offset = len_name + len_args + len_moh;
|
||||
strcpy(hook_data->app_name, app_name);/* Safe */
|
||||
if (len_args) {
|
||||
strcpy(&hook_data->app_name[hook_data->app_args_offset], app_args);/* Safe */
|
||||
}
|
||||
if (len_moh) {
|
||||
strcpy(&hook_data->app_name[hook_data->moh_offset], moh_class);/* Safe */
|
||||
}
|
||||
strcpy(&hook_data->app_name[hook_data->feature_offset], feature_name);/* Safe */
|
||||
|
||||
res = ast_bridge_dtmf_hook(features, dtmf, dynamic_dtmf_hook_trip, hook_data,
|
||||
ast_free_ptr, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
|
||||
if (res) {
|
||||
ast_free(hook_data);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int setup_dynamic_feature(void *obj, void *arg, void *data, int flags)
|
||||
{
|
||||
struct ast_applicationmap_item *item = obj;
|
||||
struct ast_bridge_features *features = arg;
|
||||
int *res = data;
|
||||
|
||||
*res |= dynamic_dtmf_hook_add(features,
|
||||
item->activate_on_self ? AST_FEATURE_FLAG_ONSELF : AST_FEATURE_FLAG_ONPEER,
|
||||
item->dtmf, item->name, item->app, item->app_data, item->moh_class);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Setup bridge dynamic features.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param features Bridge features to setup.
|
||||
* \param chan Get features from this channel.
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on error.
|
||||
*/
|
||||
static int setup_bridge_features_dynamic(struct ast_bridge_features *features, struct ast_channel *chan)
|
||||
{
|
||||
RAII_VAR(struct ao2_container *, applicationmap, NULL, ao2_cleanup);
|
||||
int res = 0;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
applicationmap = ast_get_chan_applicationmap(chan);
|
||||
ast_channel_unlock(chan);
|
||||
if (!applicationmap) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ao2_callback_data(applicationmap, 0, setup_dynamic_feature, features, &res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* BUGBUG this really should be made a private function of bridge_basic.c after struct ast_call_feature is made an ao2 object. */
|
||||
int ast_bridge_channel_setup_features(struct ast_bridge_channel *bridge_channel)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
res |= setup_bridge_features_builtin(bridge_channel->features, bridge_channel->chan);
|
||||
res |= setup_bridge_features_dynamic(bridge_channel->features, bridge_channel->chan);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void bridge_config_set_limits_warning_values(struct ast_bridge_config *config, struct ast_bridge_features_limits *limits)
|
||||
{
|
||||
if (config->end_sound) {
|
||||
|
Reference in New Issue
Block a user