summaryrefslogtreecommitdiff
path: root/apps/confbridge/conf_config_parser.c
diff options
context:
space:
mode:
authorJonathan Rose <jrose@digium.com>2013-11-01 22:48:14 +0000
committerJonathan Rose <jrose@digium.com>2013-11-01 22:48:14 +0000
commit4b7ff874923496725af9694c00fc11ddfc97a6cc (patch)
treefbcbcf25fb84e35938a844584106a3f71c75f7da /apps/confbridge/conf_config_parser.c
parent3b36687a560aab39d6bcb0c5b336a7be873f67be (diff)
app_confbridge: Make the CONFBRIDGE function be able to create dynamic menus
Also adds the ability to clear all profile items and makes behavior more consistent with documentation as when choosing whether to use CONFBRIDGE datastore profiles or the application arguments to the confbridge application. (closes issue ASTERISK-22760) Reported by: Matt Jordan Review: https://reviewboard.asterisk.org/r/2971/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@402397 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'apps/confbridge/conf_config_parser.c')
-rw-r--r--apps/confbridge/conf_config_parser.c246
1 files changed, 188 insertions, 58 deletions
diff --git a/apps/confbridge/conf_config_parser.c b/apps/confbridge/conf_config_parser.c
index 97c0696a7..5c430ea3a 100644
--- a/apps/confbridge/conf_config_parser.c
+++ b/apps/confbridge/conf_config_parser.c
@@ -419,6 +419,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</enumlist>
</description>
</configOption>
+ <configOption name="template">
+ <synopsis>When using the CONFBRIDGE dialplan function, use a menu profile as a template for creating a new temporary profile</synopsis>
+ </configOption>
<configOption name="^[0-9A-D*#]+$">
<synopsis>DTMF sequences to assign various confbridge actions to</synopsis>
<description>
@@ -896,15 +899,25 @@ static int set_sound(const char *sound_name, const char *sound_file, struct brid
struct func_confbridge_data {
struct bridge_profile b_profile;
struct user_profile u_profile;
+ struct conf_menu *menu;
unsigned int b_usable:1; /*!< Tells if bridge profile is usable or not */
unsigned int u_usable:1; /*!< Tells if user profile is usable or not */
+ unsigned int m_usable:1; /*!< Tells if menu profile is usable or not */
};
-static void func_confbridge_destroy_cb(void *data)
+
+static void func_confbridge_data_destructor(struct func_confbridge_data *b_data)
{
- struct func_confbridge_data *b_data = data;
conf_bridge_profile_destroy(&b_data->b_profile);
+ ao2_cleanup(b_data->menu);
ast_free(b_data);
+}
+
+static void func_confbridge_destroy_cb(void *data)
+{
+ struct func_confbridge_data *b_data = data;
+ func_confbridge_data_destructor(b_data);
};
+
static const struct ast_datastore_info confbridge_datastore = {
.type = "confbridge",
.destroy = func_confbridge_destroy_cb
@@ -944,14 +957,18 @@ int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data
ast_datastore_free(datastore);
return 0;
}
+ datastore->data = b_data;
b_data->b_profile.sounds = bridge_profile_sounds_alloc();
if (!b_data->b_profile.sounds) {
ast_channel_unlock(chan);
ast_datastore_free(datastore);
- ast_free(b_data);
return 0;
}
- datastore->data = b_data;
+ if (!(b_data->menu = menu_alloc("dialplan"))) {
+ ast_channel_unlock(chan);
+ ast_datastore_free(datastore);
+ return 0;
+ }
ast_channel_datastore_add(chan, datastore);
} else {
b_data = datastore->data;
@@ -966,15 +983,48 @@ int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data
tmpvar.value = value;
tmpvar.file = "CONFBRIDGE";
if (!strcasecmp(args.type, "bridge")) {
- if (!aco_process_var(&bridge_type, "dialplan", &tmpvar, &b_data->b_profile)) {
+ if (!strcasecmp(args.option, "clear")) {
+ b_data->b_usable = 0;
+ conf_bridge_profile_destroy(&b_data->b_profile);
+ memset(&b_data->b_profile, 0, sizeof(b_data->b_profile)) ;
+ if (!(b_data->b_profile.sounds = bridge_profile_sounds_alloc())) {
+ /* If this reallocation fails, the datastore has become unusable and must be destroyed. */
+ ast_channel_lock(chan);
+ ast_channel_datastore_remove(chan, datastore);
+ ast_channel_unlock(chan);
+ ast_datastore_free(datastore);
+ }
+ return 0;
+ } else if (!aco_process_var(&bridge_type, "dialplan", &tmpvar, &b_data->b_profile)) {
b_data->b_usable = 1;
return 0;
}
} else if (!strcasecmp(args.type, "user")) {
- if (!aco_process_var(&user_type, "dialplan", &tmpvar, &b_data->u_profile)) {
+ if (!strcasecmp(args.option, "clear")) {
+ b_data->u_usable = 0;
+ user_profile_destructor(&b_data->u_profile);
+ memset(&b_data->u_profile, 0, sizeof(b_data->u_profile));
+ return 0;
+ } else if (!aco_process_var(&user_type, "dialplan", &tmpvar, &b_data->u_profile)) {
b_data->u_usable = 1;
return 0;
}
+ } else if (!strcasecmp(args.type, "menu")) {
+ if (!strcasecmp(args.option, "clear")) {
+ b_data->m_usable = 0;
+ ao2_cleanup(b_data->menu);
+ if (!(b_data->menu = menu_alloc("dialplan"))) {
+ /* If this reallocation fails, the datastore has become unusable and must be destroyed */
+ ast_channel_lock(chan);
+ ast_channel_datastore_remove(chan, datastore);
+ ast_channel_unlock(chan);
+ ast_datastore_free(datastore);
+ }
+ return 0;
+ } else if (!aco_process_var(&menu_type, "dialplan", &tmpvar, b_data->menu)) {
+ b_data->m_usable = 1;
+ return 0;
+ }
}
ast_log(LOG_WARNING, "%s(%s,%s) cannot be set to '%s'. Invalid type, option, or value.\n",
@@ -1852,6 +1902,70 @@ static int bridge_template_handler(const struct aco_option *opt, struct ast_vari
return 0;
}
+static int copy_menu_entry(struct conf_menu_entry *dst, struct conf_menu_entry *src)
+{
+ struct conf_menu_action *menu_action;
+ struct conf_menu_action *new_menu_action;
+
+ ast_copy_string(dst->dtmf, src->dtmf, sizeof(dst->dtmf));
+ AST_LIST_HEAD_INIT_NOLOCK(&dst->actions);
+
+ AST_LIST_TRAVERSE(&src->actions, menu_action, action) {
+ if (!(new_menu_action = ast_calloc(1, sizeof(*new_menu_action)))) {
+ return -1;
+ }
+ memcpy(new_menu_action, menu_action, sizeof(*new_menu_action));
+ AST_LIST_NEXT(new_menu_action, action) = NULL;
+ AST_LIST_INSERT_TAIL(&dst->actions, new_menu_action, action);
+ }
+
+ return 0;
+}
+
+static int conf_menu_profile_copy(struct conf_menu *dst, struct conf_menu *src)
+{
+ /* Copy each menu item to the dst struct */
+ struct conf_menu_entry *cur;
+
+ AST_LIST_TRAVERSE(&src->entries, cur, entry) {
+ struct conf_menu_entry *cpy;
+
+ if (!(cpy = ast_calloc(1, sizeof(*cpy)))) {
+ return -1;
+ }
+
+ if (copy_menu_entry(cpy, cur)) {
+ conf_menu_entry_destroy(cpy);
+ ast_free(cpy);
+ return -1;
+ }
+ AST_LIST_INSERT_TAIL(&dst->entries, cpy, entry);
+ }
+
+ return 0;
+}
+
+static int menu_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+ struct conf_menu *dst_menu = obj;
+ struct confbridge_cfg *cfg = aco_pending_config(&cfg_info);
+ RAII_VAR(struct conf_menu *, src_menu, NULL, ao2_cleanup);
+
+ if (!cfg) {
+ return 0;
+ }
+
+ if (!(src_menu = ao2_find(cfg->menus, var->value, OBJ_KEY))) {
+ return -1;
+ }
+
+ if (conf_menu_profile_copy(dst_menu, src_menu)) {
+ return -1;
+ }
+
+ return 0;
+}
+
static int sound_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
{
set_sound(var->name, var->value, obj);
@@ -1868,6 +1982,7 @@ static int verify_default_profiles(void)
{
RAII_VAR(struct user_profile *, user_profile, NULL, ao2_cleanup);
RAII_VAR(struct bridge_profile *, bridge_profile, NULL, ao2_cleanup);
+ RAII_VAR(struct conf_menu *, menu_profile, NULL, ao2_cleanup);
struct confbridge_cfg *cfg = aco_pending_config(&cfg_info);
if (!cfg) {
@@ -1896,6 +2011,17 @@ static int verify_default_profiles(void)
ao2_link(cfg->user_profiles, user_profile);
}
+ menu_profile = ao2_find(cfg->menus, DEFAULT_MENU_PROFILE, OBJ_KEY);
+ if (!menu_profile) {
+ menu_profile = menu_alloc(DEFAULT_MENU_PROFILE);
+ if (!menu_profile) {
+ return -1;
+ }
+ ast_log(AST_LOG_NOTICE, "Adding %s menu to app_confbridge\n", DEFAULT_MENU_PROFILE);
+ aco_set_defaults(&menu_type, DEFAULT_MENU_PROFILE, menu_profile);
+ ao2_link(cfg->menus, menu_profile);
+ }
+
return 0;
}
@@ -1951,6 +2077,7 @@ int conf_load_config(void)
/* Menu options */
aco_option_register(&cfg_info, "type", ACO_EXACT, menu_types, NULL, OPT_NOOP_T, 0, 0);
+ aco_option_register_custom(&cfg_info, "template", ACO_EXACT, menu_types, NULL, menu_template_handler, 0);
aco_option_register_custom(&cfg_info, "^[0-9A-D*#]+$", ACO_REGEX, menu_types, NULL, menu_option_handler, 0);
if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
@@ -1988,11 +2115,7 @@ const struct user_profile *conf_find_user_profile(struct ast_channel *chan, cons
struct func_confbridge_data *b_data = NULL;
RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
- if (!cfg) {
- return NULL;
- }
-
- if (chan) {
+ if (chan && ast_strlen_zero(user_profile_name)) {
ast_channel_lock(chan);
datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
ast_channel_unlock(chan);
@@ -2005,6 +2128,9 @@ const struct user_profile *conf_find_user_profile(struct ast_channel *chan, cons
}
}
+ if (!cfg) {
+ return NULL;
+ }
if (ast_strlen_zero(user_profile_name)) {
user_profile_name = DEFAULT_USER_PROFILE;
}
@@ -2042,11 +2168,7 @@ const struct bridge_profile *conf_find_bridge_profile(struct ast_channel *chan,
struct func_confbridge_data *b_data = NULL;
RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
- if (!cfg) {
- return NULL;
- }
-
- if (chan) {
+ if (chan && ast_strlen_zero(bridge_profile_name)) {
ast_channel_lock(chan);
datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
ast_channel_unlock(chan);
@@ -2058,6 +2180,10 @@ const struct bridge_profile *conf_find_bridge_profile(struct ast_channel *chan,
}
}
}
+
+ if (!cfg) {
+ return NULL;
+ }
if (ast_strlen_zero(bridge_profile_name)) {
bridge_profile_name = DEFAULT_BRIDGE_PROFILE;
}
@@ -2083,7 +2209,7 @@ static void menu_hook_destroy(void *hook_pvt)
struct dtmf_menu_hook_pvt *pvt = hook_pvt;
struct conf_menu_action *action = NULL;
- ao2_ref(pvt->menu, -1);
+ ao2_cleanup(pvt->menu);
while ((action = AST_LIST_REMOVE_HEAD(&pvt->menu_entry.actions, action))) {
ast_free(action);
@@ -2098,24 +2224,6 @@ static int menu_hook_callback(struct ast_bridge_channel *bridge_channel, void *h
return conf_handle_dtmf(bridge_channel, pvt->user, &pvt->menu_entry, pvt->menu);
}
-static int copy_menu_entry(struct conf_menu_entry *dst, struct conf_menu_entry *src)
-{
- struct conf_menu_action *menu_action = NULL;
- struct conf_menu_action *new_menu_action = NULL;
-
- memcpy(dst, src, sizeof(*dst));
- AST_LIST_HEAD_INIT_NOLOCK(&dst->actions);
-
- AST_LIST_TRAVERSE(&src->actions, menu_action, action) {
- if (!(new_menu_action = ast_calloc(1, sizeof(*new_menu_action)))) {
- return -1;
- }
- memcpy(new_menu_action, menu_action, sizeof(*new_menu_action));
- AST_LIST_INSERT_HEAD(&dst->actions, new_menu_action, action);
- }
- return 0;
-}
-
void conf_menu_entry_destroy(struct conf_menu_entry *menu_entry)
{
struct conf_menu_action *menu_action = NULL;
@@ -2141,37 +2249,24 @@ int conf_find_menu_entry_by_sequence(const char *dtmf_sequence, struct conf_menu
return 0;
}
-int conf_set_menu_to_user(const char *menu_name, struct confbridge_user *user)
+static int apply_menu_hooks(struct confbridge_user *user, struct conf_menu *menu)
{
- struct conf_menu *menu;
- struct conf_menu_entry *menu_entry = NULL;
- RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
+ struct conf_menu_entry *menu_entry;
- if (!cfg) {
- return -1;
- }
-
- if (!(menu = menu_find(cfg->menus, menu_name))) {
- return -1;
- }
- ao2_lock(menu);
+ SCOPED_AO2LOCK(menu_lock, menu);
AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
struct dtmf_menu_hook_pvt *pvt;
if (!(pvt = ast_calloc(1, sizeof(*pvt)))) {
- ao2_unlock(menu);
- ao2_ref(menu, -1);
return -1;
}
+ pvt->user = user;
+ pvt->menu = ao2_bump(menu);
+
if (copy_menu_entry(&pvt->menu_entry, menu_entry)) {
- ast_free(pvt);
- ao2_unlock(menu);
- ao2_ref(menu, -1);
+ menu_hook_destroy(pvt);
return -1;
}
- pvt->user = user;
- ao2_ref(menu, +1);
- pvt->menu = menu;
if (ast_bridge_dtmf_hook(&user->features, pvt->menu_entry.dtmf,
menu_hook_callback, pvt, menu_hook_destroy, 0)) {
@@ -2179,12 +2274,47 @@ int conf_set_menu_to_user(const char *menu_name, struct confbridge_user *user)
}
}
- ao2_unlock(menu);
- ao2_ref(menu, -1);
-
return 0;
}
+int conf_set_menu_to_user(struct ast_channel *chan, struct confbridge_user *user, const char *menu_profile_name)
+{
+ RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
+ RAII_VAR(struct conf_menu *, menu, NULL, ao2_cleanup);
+
+ if (chan && ast_strlen_zero(menu_profile_name)) {
+ struct ast_datastore *datastore;
+ struct func_confbridge_data *b_data;
+
+ ast_channel_lock(chan);
+ datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
+ ast_channel_unlock(chan);
+ if (datastore) {
+ /* If a menu exists in the CONFBRIDGE function datastore, use it. */
+ b_data = datastore->data;
+ if (b_data->m_usable) {
+ menu = ao2_bump(b_data->menu);
+ return apply_menu_hooks(user, menu);
+ }
+ }
+ }
+
+ /* Otherwise, we need to get whatever menu profile is specified to use (or default). */
+ if (!cfg) {
+ return -1;
+ }
+
+ if (ast_strlen_zero(menu_profile_name)) {
+ menu_profile_name = DEFAULT_MENU_PROFILE;
+ }
+
+ if (!(menu = ao2_find(cfg->menus, menu_profile_name, OBJ_KEY))) {
+ return -1;
+ }
+
+ return apply_menu_hooks(user, menu);
+}
+
void conf_destroy_config(void)
{
ast_cli_unregister_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser));