diff --git a/apps/confbridge/conf_config_parser.c b/apps/confbridge/conf_config_parser.c index d0a436a9f4..dc81e3cc85 100644 --- a/apps/confbridge/conf_config_parser.c +++ b/apps/confbridge/conf_config_parser.c @@ -32,6 +32,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/logger.h" #include "asterisk/config.h" +#include "asterisk/config_options.h" #include "include/confbridge.h" #include "asterisk/astobj2.h" #include "asterisk/cli.h" @@ -39,58 +40,175 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/stringfields.h" #include "asterisk/pbx.h" -#define CONFBRIDGE_CONFIG "confbridge.conf" +struct confbridge_cfg { + struct ao2_container *bridge_profiles; + struct ao2_container *user_profiles; + struct ao2_container *menus; +}; -static struct ao2_container *user_profiles; -static struct ao2_container *bridge_profiles; -static struct ao2_container *menus; +static void *bridge_profile_alloc(const char *category); +static void *bridge_profile_find(struct ao2_container *container, const char *category); +static struct bridge_profile_sounds *bridge_profile_sounds_alloc(void); -/*! bridge profile container functions */ -static int bridge_cmp_cb(void *obj, void *arg, int flags) +static void bridge_profile_destructor(void *obj) { - const struct bridge_profile *entry1 = obj; - const struct bridge_profile *entry2 = arg; - return (!strcasecmp(entry1->name, entry2->name)) ? CMP_MATCH | CMP_STOP : 0; + struct bridge_profile *b_profile = obj; + ao2_cleanup(b_profile->sounds); } -static int bridge_hash_cb(const void *obj, const int flags) + +static void *bridge_profile_alloc(const char *category) { - const struct bridge_profile *b_profile = obj; - return ast_str_case_hash(b_profile->name); + struct bridge_profile *b_profile; + + if (!(b_profile = ao2_alloc(sizeof(*b_profile), bridge_profile_destructor))) { + return NULL; + } + + if (!(b_profile->sounds = bridge_profile_sounds_alloc())) { + ao2_ref(b_profile, -1); + return NULL; + } + + ast_copy_string(b_profile->name, category, sizeof(b_profile->name)); + + return b_profile; } -static int bridge_mark_delme_cb(void *obj, void *arg, int flag) + +static void *bridge_profile_find(struct ao2_container *container, const char *category) { - struct bridge_profile *entry = obj; - entry->delme = 1; - return 0; + return ao2_find(container, category, OBJ_KEY); } -static int match_bridge_delme_cb(void *obj, void *arg, int flag) + +static struct aco_type bridge_type = { + .type = ACO_ITEM, + .category_match = ACO_BLACKLIST, + .category = "^general$", + .matchfield = "type", + .matchvalue = "bridge", + .item_alloc = bridge_profile_alloc, + .item_find = bridge_profile_find, + .item_offset = offsetof(struct confbridge_cfg, bridge_profiles), +}; + +static void *user_profile_alloc(const char *category); +static void *user_profile_find(struct ao2_container *container, const char *category); +static void user_profile_destructor(void *obj) { - const struct bridge_profile *entry = obj; - return entry->delme ? CMP_MATCH : 0; + return; } -/*! menu container functions */ -static int menu_cmp_cb(void *obj, void *arg, int flags) +static void *user_profile_alloc(const char *category) { - const struct conf_menu *entry1 = obj; - const struct conf_menu *entry2 = arg; - return (!strcasecmp(entry1->name, entry2->name)) ? CMP_MATCH | CMP_STOP : 0; + struct user_profile *u_profile; + + if (!(u_profile = ao2_alloc(sizeof(*u_profile), user_profile_destructor))) { + return NULL; + } + + ast_copy_string(u_profile->name, category, sizeof(u_profile->name)); + + return u_profile; } -static int menu_hash_cb(const void *obj, const int flags) + +static void *user_profile_find(struct ao2_container *container, const char *category) { - const struct conf_menu *menu = obj; - return ast_str_case_hash(menu->name); + return ao2_find(container, category, OBJ_KEY); } -static int menu_mark_delme_cb(void *obj, void *arg, int flag) + +static struct aco_type user_type = { + .type = ACO_ITEM, + .category_match = ACO_BLACKLIST, + .category = "^general$", + .matchfield = "type", + .matchvalue = "user", + .item_alloc = user_profile_alloc, + .item_find = user_profile_find, + .item_offset = offsetof(struct confbridge_cfg, user_profiles), +}; + +static void *menu_alloc(const char *category); +static void *menu_find(struct ao2_container *container, const char *category); +static void menu_destructor(void *obj); + +static void *menu_alloc(const char *category) { - struct conf_menu *entry = obj; - entry->delme = 1; - return 0; + struct conf_menu *menu; + if (!(menu = ao2_alloc(sizeof(*menu), menu_destructor))) { + return NULL; + } + ast_copy_string(menu->name, category, sizeof(menu->name)); + return menu; +} + +static void *menu_find(struct ao2_container *container, const char *category) +{ + return ao2_find(container, category, OBJ_KEY); +} + +static struct aco_type menu_type = { + .type = ACO_ITEM, + .category_match = ACO_BLACKLIST, + .category = "^general$", + .matchfield = "type", + .matchvalue = "menu", + .item_alloc = menu_alloc, + .item_find = menu_find, + .item_offset = offsetof(struct confbridge_cfg, menus), +}; + +/* Used to pass to aco_option_register */ +static struct aco_type *bridge_types[] = ACO_TYPES(&bridge_type); +static struct aco_type *menu_types[] = ACO_TYPES(&menu_type); +static struct aco_type *user_types[] = ACO_TYPES(&user_type); + +/* The general category is reserved, but unused */ +static struct aco_type general_type = { + .type = ACO_GLOBAL, + .category_match = ACO_WHITELIST, + .category = "^general$", +}; + +static struct aco_file confbridge_conf = { + .filename = "confbridge.conf", + .types = ACO_TYPES(&bridge_type, &user_type, &menu_type, &general_type), +}; + +static AO2_GLOBAL_OBJ_STATIC(cfg_handle); + +static void *confbridge_cfg_alloc(void); + +CONFIG_INFO_STANDARD(cfg_info, cfg_handle, confbridge_cfg_alloc, + .files = ACO_FILES(&confbridge_conf), +); + +/*! bridge profile container functions */ +static int bridge_cmp_cb(void *obj, void *arg, int flags) +{ + const struct bridge_profile *entry1 = obj, *entry2 = arg; + const char *name = arg; + return (!strcasecmp(entry1->name, flags & OBJ_KEY ? name : entry2->name)) ? + CMP_MATCH | CMP_STOP : 0; +} +static int bridge_hash_cb(const void *obj, const int flags) +{ + const struct bridge_profile *b_profile = obj; + const char *name = obj; + return ast_str_case_hash(flags & OBJ_KEY ? name : b_profile->name); +} + +/*! menu container functions */ +static int menu_cmp_cb(void *obj, void *arg, int flags) +{ + const struct conf_menu *entry1 = obj, *entry2 = arg; + const char *name = arg; + return (!strcasecmp(entry1->name, flags & OBJ_KEY ? name : entry2->name)) ? + CMP_MATCH | CMP_STOP : 0; } -static int match_menu_delme_cb(void *obj, void *arg, int flag) +static int menu_hash_cb(const void *obj, const int flags) { - const struct conf_menu *entry = obj; - return entry->delme ? CMP_MATCH : 0; + const struct conf_menu *menu = obj; + const char *name = obj; + return ast_str_case_hash(flags & OBJ_KEY ? name : menu->name); } static void menu_destructor(void *obj) { @@ -106,25 +224,16 @@ static void menu_destructor(void *obj) /*! User profile container functions */ static int user_cmp_cb(void *obj, void *arg, int flags) { - const struct user_profile *entry1 = obj; - const struct user_profile *entry2 = arg; - return (!strcasecmp(entry1->name, entry2->name)) ? CMP_MATCH | CMP_STOP : 0; + const struct user_profile *entry1 = obj, *entry2 = arg; + const char *name = arg; + return (!strcasecmp(entry1->name, flags & OBJ_KEY ? name : entry2->name)) ? + CMP_MATCH | CMP_STOP : 0; } static int user_hash_cb(const void *obj, const int flags) { const struct user_profile *u_profile = obj; - return ast_str_case_hash(u_profile->name); -} -static int user_mark_delme_cb(void *obj, void *arg, int flag) -{ - struct user_profile *entry = obj; - entry->delme = 1; - return 0; -} -static int match_user_delme_cb(void *obj, void *arg, int flag) -{ - const struct user_profile *entry = obj; - return entry->delme ? CMP_MATCH : 0; + const char *name = obj; + return ast_str_case_hash(flags & OBJ_KEY ? name : u_profile->name); } /*! Bridge Profile Sounds functions */ @@ -149,76 +258,9 @@ static struct bridge_profile_sounds *bridge_profile_sounds_alloc(void) return sounds; } -static int set_user_option(const char *name, const char *value, struct user_profile *u_profile) -{ - if (!strcasecmp(name, "admin")) { - ast_set2_flag(u_profile, ast_true(value), USER_OPT_ADMIN); - } else if (!strcasecmp(name, "marked")) { - ast_set2_flag(u_profile, ast_true(value), USER_OPT_MARKEDUSER); - } else if (!strcasecmp(name, "startmuted")) { - ast_set2_flag(u_profile, ast_true(value), USER_OPT_STARTMUTED); - } else if (!strcasecmp(name, "music_on_hold_when_empty")) { - ast_set2_flag(u_profile, ast_true(value), USER_OPT_MUSICONHOLD); - } else if (!strcasecmp(name, "quiet")) { - ast_set2_flag(u_profile, ast_true(value), USER_OPT_QUIET); - } else if (!strcasecmp(name, "announce_user_count_all")) { - if (ast_true(value)) { - u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL; - } else if (ast_false(value)) { - u_profile->flags = u_profile->flags & ~USER_OPT_ANNOUNCEUSERCOUNTALL; - } else if (sscanf(value, "%30u", &u_profile->announce_user_count_all_after) == 1) { - u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL; - } else { - return -1; - } - } else if (!strcasecmp(name, "announce_user_count")) { - ast_set2_flag(u_profile, ast_true(value), USER_OPT_ANNOUNCEUSERCOUNT); - } else if (!strcasecmp(name, "announce_only_user")) { - u_profile->flags = ast_true(value) ? - u_profile->flags & ~USER_OPT_NOONLYPERSON : - u_profile->flags | USER_OPT_NOONLYPERSON; - } else if (!strcasecmp(name, "wait_marked")) { - ast_set2_flag(u_profile, ast_true(value), USER_OPT_WAITMARKED); - } else if (!strcasecmp(name, "end_marked")) { - ast_set2_flag(u_profile, ast_true(value), USER_OPT_ENDMARKED); - } else if (!strcasecmp(name, "talk_detection_events")) { - ast_set2_flag(u_profile, ast_true(value), USER_OPT_TALKER_DETECT); - } else if (!strcasecmp(name, "dtmf_passthrough")) { - ast_set2_flag(u_profile, ast_true(value), USER_OPT_DTMF_PASS); - } else if (!strcasecmp(name, "announce_join_leave")) { - ast_set2_flag(u_profile, ast_true(value), USER_OPT_ANNOUNCE_JOIN_LEAVE); - } else if (!strcasecmp(name, "pin")) { - ast_copy_string(u_profile->pin, value, sizeof(u_profile->pin)); - } else if (!strcasecmp(name, "music_on_hold_class")) { - ast_copy_string(u_profile->moh_class, value, sizeof(u_profile->moh_class)); - } else if (!strcasecmp(name, "announcement")) { - ast_copy_string(u_profile->announcement, value, sizeof(u_profile->announcement)); - } else if (!strcasecmp(name, "denoise")) { - ast_set2_flag(u_profile, ast_true(value), USER_OPT_DENOISE); - } else if (!strcasecmp(name, "dsp_talking_threshold")) { - if (sscanf(value, "%30u", &u_profile->talking_threshold) != 1) { - return -1; - } - } else if (!strcasecmp(name, "dsp_silence_threshold")) { - if (sscanf(value, "%30u", &u_profile->silence_threshold) != 1) { - return -1; - } - } else if (!strcasecmp(name, "dsp_drop_silence")) { - ast_set2_flag(u_profile, ast_true(value), USER_OPT_DROP_SILENCE); - } else if (!strcasecmp(name, "template")) { - if (!(conf_find_user_profile(NULL, value, u_profile))) { - return -1; - } - } else if (!strcasecmp(name, "jitterbuffer")) { - ast_set2_flag(u_profile, ast_true(value), USER_OPT_JITTERBUFFER); - } else { - return -1; - } - return 0; -} - -static int set_sound(const char *sound_name, const char *sound_file, struct bridge_profile_sounds *sounds) +static int set_sound(const char *sound_name, const char *sound_file, struct bridge_profile *b_profile) { + struct bridge_profile_sounds *sounds = b_profile->sounds; if (ast_strlen_zero(sound_file)) { return -1; } @@ -273,92 +315,6 @@ static int set_sound(const char *sound_name, const char *sound_file, struct brid return 0; } -static int set_bridge_option(const char *name, const char *value, struct bridge_profile *b_profile) -{ - if (!strcasecmp(name, "internal_sample_rate")) { - if (!strcasecmp(value, "auto")) { - b_profile->internal_sample_rate = 0; - } else if (sscanf(value, "%30u", &b_profile->internal_sample_rate) != 1) { - return -1; - } - } else if (!strcasecmp(name, "mixing_interval")) { - if (sscanf(value, "%30u", &b_profile->mix_interval) != 1) { - return -1; - } - switch (b_profile->mix_interval) { - case 10: - case 20: - case 40: - case 80: - break; - default: - ast_log(LOG_WARNING, "invalid mixing interval %u\n", b_profile->mix_interval); - b_profile->mix_interval = 0; - return -1; - } - } else if (!strcasecmp(name, "record_conference")) { - ast_set2_flag(b_profile, ast_true(value), BRIDGE_OPT_RECORD_CONFERENCE); - } else if (!strcasecmp(name, "video_mode")) { - if (!strcasecmp(value, "first_marked")) { - ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED); - } else if (!strcasecmp(value, "last_marked")) { - ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED); - } else if (!strcasecmp(value, "follow_talker")) { - ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER); - } - } else if (!strcasecmp(name, "max_members")) { - if (sscanf(value, "%30u", &b_profile->max_members) != 1) { - return -1; - } - } else if (!strcasecmp(name, "record_file")) { - ast_copy_string(b_profile->rec_file, value, sizeof(b_profile->rec_file)); - } else if (strlen(name) >= 5 && !strncasecmp(name, "sound", 5)) { - if (set_sound(name, value, b_profile->sounds)) { - return -1; - } - } else if (!strcasecmp(name, "template")) { /* Only documented for use in CONFBRIDGE dialplan function */ - struct bridge_profile *tmp = b_profile; - struct bridge_profile_sounds *sounds = bridge_profile_sounds_alloc(); - struct bridge_profile_sounds *oldsounds = b_profile->sounds; - if (!sounds) { - return -1; - } - if (!(conf_find_bridge_profile(NULL, value, tmp))) { - ao2_ref(sounds, -1); - return -1; - } - /* Using a bridge profile as a template is a little complicated due to the sounds. Since the sounds - * structure of a dynamic profile will need to be altered, a completely new sounds structure must be - * created instead of simply holding a reference to the one built by the config file. */ - ast_string_field_set(sounds, onlyperson, tmp->sounds->onlyperson); - ast_string_field_set(sounds, hasjoin, tmp->sounds->hasjoin); - ast_string_field_set(sounds, hasleft, tmp->sounds->hasleft); - ast_string_field_set(sounds, kicked, tmp->sounds->kicked); - ast_string_field_set(sounds, muted, tmp->sounds->muted); - ast_string_field_set(sounds, unmuted, tmp->sounds->unmuted); - ast_string_field_set(sounds, thereare, tmp->sounds->thereare); - ast_string_field_set(sounds, otherinparty, tmp->sounds->otherinparty); - ast_string_field_set(sounds, placeintoconf, tmp->sounds->placeintoconf); - ast_string_field_set(sounds, waitforleader, tmp->sounds->waitforleader); - ast_string_field_set(sounds, leaderhasleft, tmp->sounds->leaderhasleft); - ast_string_field_set(sounds, getpin, tmp->sounds->getpin); - ast_string_field_set(sounds, invalidpin, tmp->sounds->invalidpin); - ast_string_field_set(sounds, locked, tmp->sounds->locked); - ast_string_field_set(sounds, unlockednow, tmp->sounds->unlockednow); - ast_string_field_set(sounds, lockednow, tmp->sounds->lockednow); - ast_string_field_set(sounds, errormenu, tmp->sounds->errormenu); - ast_string_field_set(sounds, participantsmuted, tmp->sounds->participantsmuted); - ast_string_field_set(sounds, participantsunmuted, tmp->sounds->participantsunmuted); - - ao2_ref(tmp->sounds, -1); /* sounds struct copied over to it from the template by reference only. */ - ao2_ref(oldsounds,-1); /* original sounds struct we don't need anymore */ - tmp->sounds = sounds; /* the new sounds struct that is a deep copy of the one from the template. */ - } else { - return -1; - } - - return 0; -} /*! CONFBRIDGE dialplan function functions and channel datastore. */ struct func_confbridge_data { @@ -383,6 +339,7 @@ int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data struct func_confbridge_data *b_data = NULL; char *parse = NULL; int new = 0; + struct ast_variable tmpvar = { 0, }; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(type); AST_APP_ARG(option); @@ -421,10 +378,13 @@ int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data b_data = datastore->data; } + tmpvar.name = args.option; + tmpvar.value = value; + tmpvar.file = "CONFBRIDGE"; /* SET(CONFBRIDGE(type,option)=value) */ - if (!strcasecmp(args.type, "bridge") && !set_bridge_option(args.option, value, &b_data->b_profile)) { + if (!strcasecmp(args.type, "bridge") && !aco_process_var(&bridge_type, "dialplan", &tmpvar, &b_data->b_profile)) { b_data->b_usable = 1; - } else if (!strcasecmp(args.type, "user") && !set_user_option(args.option, value, &b_data->u_profile)) { + } else if (!strcasecmp(args.type, "user") && !aco_process_var(&user_type, "dialplan", &tmpvar, &b_data->u_profile)) { b_data->u_usable = 1; } else { ast_log(LOG_WARNING, "Profile type \"%s\" can not be set in CONFBRIDGE function with option \"%s\" and value \"%s\"\n", @@ -446,99 +406,6 @@ cleanup_error: return -1; } -/*! - * \brief Parse the bridge profile options - */ -static int parse_bridge(const char *cat, struct ast_config *cfg) -{ - struct ast_variable *var; - struct bridge_profile tmp; - struct bridge_profile *b_profile; - - ast_copy_string(tmp.name, cat, sizeof(tmp.name)); - if ((b_profile = ao2_find(bridge_profiles, &tmp, OBJ_POINTER))) { - b_profile->delme = 0; - } else if ((b_profile = ao2_alloc(sizeof(*b_profile), NULL))) { - ast_copy_string(b_profile->name, cat, sizeof(b_profile->name)); - ao2_link(bridge_profiles, b_profile); - } else { - return -1; - } - - ao2_lock(b_profile); - /* set defaults */ - b_profile->internal_sample_rate = 0; - b_profile->flags = 0; - b_profile->max_members = 0; - b_profile->mix_interval = 0; - memset(b_profile->rec_file, 0, sizeof(b_profile->rec_file)); - if (b_profile->sounds) { - ao2_ref(b_profile->sounds, -1); /* sounds is read only. Once it has been created - * it can never be altered. This prevents having to - * do any locking after it is built from the config. */ - b_profile->sounds = NULL; - } - - if (!(b_profile->sounds = bridge_profile_sounds_alloc())) { - ao2_unlock(b_profile); - ao2_ref(b_profile, -1); - ao2_unlink(bridge_profiles, b_profile); - return -1; - } - - for (var = ast_variable_browse(cfg, cat); var; var = var->next) { - if (!strcasecmp(var->name, "type")) { - continue; - } else if (set_bridge_option(var->name, var->value, b_profile)) { - ast_log(LOG_WARNING, "Invalid: '%s' at line %d of %s is not supported.\n", - var->name, var->lineno, CONFBRIDGE_CONFIG); - } - } - ao2_unlock(b_profile); - - ao2_ref(b_profile, -1); - return 0; -} - -static int parse_user(const char *cat, struct ast_config *cfg) -{ - struct ast_variable *var; - struct user_profile tmp; - struct user_profile *u_profile; - - ast_copy_string(tmp.name, cat, sizeof(tmp.name)); - if ((u_profile = ao2_find(user_profiles, &tmp, OBJ_POINTER))) { - u_profile->delme = 0; - } else if ((u_profile = ao2_alloc(sizeof(*u_profile), NULL))) { - ast_copy_string(u_profile->name, cat, sizeof(u_profile->name)); - ao2_link(user_profiles, u_profile); - } else { - return -1; - } - - ao2_lock(u_profile); - /* set defaults */ - u_profile->flags = 0; - u_profile->announce_user_count_all_after = 0; - u_profile->silence_threshold = DEFAULT_SILENCE_THRESHOLD; - u_profile->talking_threshold = DEFAULT_TALKING_THRESHOLD; - memset(u_profile->pin, 0, sizeof(u_profile->pin)); - memset(u_profile->moh_class, 0, sizeof(u_profile->moh_class)); - memset(u_profile->announcement, 0, sizeof(u_profile->announcement)); - for (var = ast_variable_browse(cfg, cat); var; var = var->next) { - if (!strcasecmp(var->name, "type")) { - continue; - } else if (set_user_option(var->name, var->value, u_profile)) { - ast_log(LOG_WARNING, "Invalid option '%s' at line %d of %s is not supported.\n", - var->name, var->lineno, CONFBRIDGE_CONFIG); - } - } - ao2_unlock(u_profile); - - ao2_ref(u_profile, -1); - return 0; -} - static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum conf_menu_action_id id, char *databuf) { struct conf_menu_action *menu_action = ast_calloc(1, sizeof(*menu_action)); @@ -743,38 +610,6 @@ static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char * return 0; } -static int parse_menu(const char *cat, struct ast_config *cfg) -{ - struct ast_variable *var; - struct conf_menu tmp; - struct conf_menu *menu; - - ast_copy_string(tmp.name, cat, sizeof(tmp.name)); - if ((menu = ao2_find(menus, &tmp, OBJ_POINTER))) { - menu->delme = 0; - } else if ((menu = ao2_alloc(sizeof(*menu), menu_destructor))) { - ast_copy_string(menu->name, cat, sizeof(menu->name)); - ao2_link(menus, menu); - } else { - return -1; - } - - ao2_lock(menu); - /* this isn't freeing the menu, just destroying the menu list so it can be rebuilt.*/ - menu_destructor(menu); - for (var = ast_variable_browse(cfg, cat); var; var = var->next) { - if (!strcasecmp(var->name, "type")) { - continue; - } else if (add_menu_entry(menu, var->name, var->value)) { - ast_log(LOG_WARNING, "Unknown option '%s' at line %d of %s is not supported.\n", - var->name, var->lineno, CONFBRIDGE_CONFIG); - } - } - ao2_unlock(menu); - - ao2_ref(menu, -1); - return 0; -} static char *complete_user_profile_name(const char *line, const char *word, int pos, int state) { @@ -783,8 +618,13 @@ static char *complete_user_profile_name(const char *line, const char *word, int int wordlen = strlen(word); struct ao2_iterator i; struct user_profile *u_profile = NULL; + RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup); + + if (!cfg) { + return NULL; + } - i = ao2_iterator_init(user_profiles, 0); + i = ao2_iterator_init(cfg->user_profiles, 0); while ((u_profile = ao2_iterator_next(&i))) { if (!strncasecmp(u_profile->name, word, wordlen) && ++which > state) { res = ast_strdup(u_profile->name); @@ -802,6 +642,7 @@ static char *handle_cli_confbridge_show_user_profiles(struct ast_cli_entry *e, i { struct ao2_iterator it; struct user_profile *u_profile; + RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup); switch (cmd) { case CLI_INIT: @@ -813,15 +654,19 @@ static char *handle_cli_confbridge_show_user_profiles(struct ast_cli_entry *e, i return NULL; } + if (!(cfg = ao2_global_obj_ref(cfg_handle))) { + return NULL; + } + ast_cli(a->fd,"--------- User Profiles -----------\n"); - ao2_lock(user_profiles); - it = ao2_iterator_init(user_profiles, 0); + ao2_lock(cfg->user_profiles); + it = ao2_iterator_init(cfg->user_profiles, 0); while ((u_profile = ao2_iterator_next(&it))) { ast_cli(a->fd,"%s\n", u_profile->name); ao2_ref(u_profile, -1); } ao2_iterator_destroy(&it); - ao2_unlock(user_profiles); + ao2_unlock(cfg->user_profiles); return CLI_SUCCESS; } @@ -923,8 +768,13 @@ static char *complete_bridge_profile_name(const char *line, const char *word, in int wordlen = strlen(word); struct ao2_iterator i; struct bridge_profile *b_profile = NULL; + RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup); + + if (!cfg) { + return NULL; + } - i = ao2_iterator_init(bridge_profiles, 0); + i = ao2_iterator_init(cfg->bridge_profiles, 0); while ((b_profile = ao2_iterator_next(&i))) { if (!strncasecmp(b_profile->name, word, wordlen) && ++which > state) { res = ast_strdup(b_profile->name); @@ -942,6 +792,7 @@ static char *handle_cli_confbridge_show_bridge_profiles(struct ast_cli_entry *e, { struct ao2_iterator it; struct bridge_profile *b_profile; + RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup); switch (cmd) { case CLI_INIT: @@ -953,15 +804,19 @@ static char *handle_cli_confbridge_show_bridge_profiles(struct ast_cli_entry *e, return NULL; } + if (!(cfg = ao2_global_obj_ref(cfg_handle))) { + return NULL; + } + ast_cli(a->fd,"--------- Bridge Profiles -----------\n"); - ao2_lock(bridge_profiles); - it = ao2_iterator_init(bridge_profiles, 0); + ao2_lock(cfg->bridge_profiles); + it = ao2_iterator_init(cfg->bridge_profiles, 0); while ((b_profile = ao2_iterator_next(&it))) { ast_cli(a->fd,"%s\n", b_profile->name); ao2_ref(b_profile, -1); } ao2_iterator_destroy(&it); - ao2_unlock(bridge_profiles); + ao2_unlock(cfg->bridge_profiles); return CLI_SUCCESS; } @@ -1067,8 +922,13 @@ static char *complete_menu_name(const char *line, const char *word, int pos, int int wordlen = strlen(word); struct ao2_iterator i; struct conf_menu *menu = NULL; + RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup); + + if (!cfg) { + return NULL; + } - i = ao2_iterator_init(menus, 0); + i = ao2_iterator_init(cfg->menus, 0); while ((menu = ao2_iterator_next(&i))) { if (!strncasecmp(menu->name, word, wordlen) && ++which > state) { res = ast_strdup(menu->name); @@ -1086,6 +946,7 @@ static char *handle_cli_confbridge_show_menus(struct ast_cli_entry *e, int cmd, { struct ao2_iterator it; struct conf_menu *menu; + RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup); switch (cmd) { case CLI_INIT: @@ -1097,23 +958,27 @@ static char *handle_cli_confbridge_show_menus(struct ast_cli_entry *e, int cmd, return NULL; } + if (!(cfg = ao2_global_obj_ref(cfg_handle))) { + return NULL; + } + ast_cli(a->fd,"--------- Menus -----------\n"); - ao2_lock(menus); - it = ao2_iterator_init(menus, 0); + ao2_lock(cfg->menus); + it = ao2_iterator_init(cfg->menus, 0); while ((menu = ao2_iterator_next(&it))) { ast_cli(a->fd,"%s\n", menu->name); ao2_ref(menu, -1); } ao2_iterator_destroy(&it); - ao2_unlock(menus); + ao2_unlock(cfg->menus); return CLI_SUCCESS; } static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - struct conf_menu tmp; - struct conf_menu *menu; + RAII_VAR(struct conf_menu *, menu, NULL, ao2_cleanup); + RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup); struct conf_menu_entry *menu_entry = NULL; struct conf_menu_action *menu_action = NULL; @@ -1134,8 +999,11 @@ static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, s return CLI_SHOWUSAGE; } - ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name)); - if (!(menu = ao2_find(menus, &tmp, OBJ_POINTER))) { + if (!(cfg = ao2_global_obj_ref(cfg_handle))) { + return NULL; + } + + if (!(menu = menu_find(cfg->menus, a->argv[3]))) { ast_cli(a->fd, "No conference menu named '%s' found!\n", a->argv[3]); return CLI_SUCCESS; } @@ -1215,7 +1083,6 @@ static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, s ao2_unlock(menu); - ao2_ref(menu, -1); return CLI_SUCCESS; } @@ -1229,99 +1096,221 @@ static struct ast_cli_entry cli_confbridge_parser[] = { }; -static int conf_parse_init(void) +static void confbridge_cfg_destructor(void *obj) { - if (!(user_profiles = ao2_container_alloc(283, user_hash_cb, user_cmp_cb))) { - conf_destroy_config(); - return -1; + struct confbridge_cfg *cfg = obj; + ao2_cleanup(cfg->user_profiles); + ao2_cleanup(cfg->bridge_profiles); + ao2_cleanup(cfg->menus); +} + +void *confbridge_cfg_alloc(void) +{ + struct confbridge_cfg *cfg; + + if (!(cfg = ao2_alloc(sizeof(*cfg), confbridge_cfg_destructor))) { + return NULL; } - if (!(bridge_profiles = ao2_container_alloc(283, bridge_hash_cb, bridge_cmp_cb))) { - conf_destroy_config(); - return -1; + if (!(cfg->user_profiles = ao2_container_alloc(283, user_hash_cb, user_cmp_cb))) { + goto error; } - if (!(menus = ao2_container_alloc(283, menu_hash_cb, menu_cmp_cb))) { - conf_destroy_config(); - return -1; + if (!(cfg->bridge_profiles = ao2_container_alloc(283, bridge_hash_cb, bridge_cmp_cb))) { + goto error; } - ast_cli_register_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser)); + if (!(cfg->menus = ao2_container_alloc(283, menu_hash_cb, menu_cmp_cb))) { + goto error; + } - return 0; + return cfg; +error: + ao2_ref(cfg, -1); + return NULL; } -void conf_destroy_config() +static int announce_user_count_all_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { - if (user_profiles) { - ao2_ref(user_profiles, -1); - user_profiles = NULL; + struct user_profile *u_profile = obj; + + if (strcasecmp(var->name, "announce_user_count_all")) { + return -1; } - if (bridge_profiles) { - ao2_ref(bridge_profiles, -1); - bridge_profiles = NULL; + if (ast_true(var->value)) { + u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL; + } else if (ast_false(var->value)) { + u_profile->flags = u_profile->flags & ~USER_OPT_ANNOUNCEUSERCOUNTALL; + } else if (sscanf(var->value, "%30u", &u_profile->announce_user_count_all_after) == 1) { + u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL; + } else { + return -1; } + return 0; +} + +static int mix_interval_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct bridge_profile *b_profile = obj; - if (menus) { - ao2_ref(menus, -1); - menus = NULL; + if (strcasecmp(var->name, "mixing_interval")) { + return -1; + } + if (sscanf(var->value, "%30u", &b_profile->mix_interval) != 1) { + return -1; + } + switch (b_profile->mix_interval) { + case 10: + case 20: + case 40: + case 80: + return 0; + default: + return -1; } - ast_cli_unregister_multiple(cli_confbridge_parser, sizeof(cli_confbridge_parser) / sizeof(struct ast_cli_entry)); } -static void remove_all_delme(void) +static int video_mode_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { - ao2_callback(user_profiles, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, match_user_delme_cb, NULL); - ao2_callback(bridge_profiles, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, match_bridge_delme_cb, NULL); - ao2_callback(menus, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, match_menu_delme_cb, NULL); + struct bridge_profile *b_profile = obj; + + if (strcasecmp(var->name, "video_mode")) { + return -1; + } + if (!strcasecmp(var->value, "first_marked")) { + ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED); + } else if (!strcasecmp(var->value, "last_marked")) { + ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED); + } else if (!strcasecmp(var->value, "follow_talker")) { + ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER); + } else { + return -1; + } + return 0; } -static void mark_all_delme(void) +static int user_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { - ao2_callback(user_profiles, OBJ_NODATA | OBJ_MULTIPLE, user_mark_delme_cb, NULL); - ao2_callback(bridge_profiles, OBJ_NODATA | OBJ_MULTIPLE, bridge_mark_delme_cb, NULL); - ao2_callback(menus, OBJ_NODATA | OBJ_MULTIPLE, menu_mark_delme_cb, NULL); + struct user_profile *u_profile = obj; + + return conf_find_user_profile(NULL, var->value, u_profile) ? 0 : -1; } -int conf_load_config(int reload) +static int bridge_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { - struct ast_flags config_flags = { 0, }; - struct ast_config *cfg = ast_config_load(CONFBRIDGE_CONFIG, config_flags); - const char *type = NULL; - char *cat = NULL; + struct bridge_profile *b_profile = obj; + struct bridge_profile_sounds *sounds = bridge_profile_sounds_alloc(); + struct bridge_profile_sounds *oldsounds = b_profile->sounds; - if (!reload) { - conf_parse_init(); + if (!sounds) { + return -1; } - - if (!cfg || cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) { - return 0; + if (!(conf_find_bridge_profile(NULL, var->value, b_profile))) { + ao2_ref(sounds, -1); + return -1; } + /* Using a bridge profile as a template is a little complicated due to the sounds. Since the sounds + * structure of a dynamic profile will need to be altered, a completely new sounds structure must be + * created instead of simply holding a reference to the one built by the config file. */ + ast_string_field_set(sounds, onlyperson, b_profile->sounds->onlyperson); + ast_string_field_set(sounds, hasjoin, b_profile->sounds->hasjoin); + ast_string_field_set(sounds, hasleft, b_profile->sounds->hasleft); + ast_string_field_set(sounds, kicked, b_profile->sounds->kicked); + ast_string_field_set(sounds, muted, b_profile->sounds->muted); + ast_string_field_set(sounds, unmuted, b_profile->sounds->unmuted); + ast_string_field_set(sounds, thereare, b_profile->sounds->thereare); + ast_string_field_set(sounds, otherinparty, b_profile->sounds->otherinparty); + ast_string_field_set(sounds, placeintoconf, b_profile->sounds->placeintoconf); + ast_string_field_set(sounds, waitforleader, b_profile->sounds->waitforleader); + ast_string_field_set(sounds, leaderhasleft, b_profile->sounds->leaderhasleft); + ast_string_field_set(sounds, getpin, b_profile->sounds->getpin); + ast_string_field_set(sounds, invalidpin, b_profile->sounds->invalidpin); + ast_string_field_set(sounds, locked, b_profile->sounds->locked); + ast_string_field_set(sounds, unlockednow, b_profile->sounds->unlockednow); + ast_string_field_set(sounds, lockednow, b_profile->sounds->lockednow); + ast_string_field_set(sounds, errormenu, b_profile->sounds->errormenu); + ast_string_field_set(sounds, participantsmuted, b_profile->sounds->participantsmuted); + ast_string_field_set(sounds, participantsunmuted, b_profile->sounds->participantsunmuted); + + ao2_ref(b_profile->sounds, -1); /* sounds struct copied over to it from the template by reference only. */ + ao2_ref(oldsounds,-1); /* original sounds struct we don't need anymore */ + b_profile->sounds = sounds; /* the new sounds struct that is a deep copy of the one from the template. */ - mark_all_delme(); + return 0; +} - while ((cat = ast_category_browse(cfg, cat))) { - if (!(type = (ast_variable_retrieve(cfg, cat, "type")))) { - if (strcasecmp(cat, "general")) { - ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat); - } - continue; +static int sound_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + set_sound(var->name, var->value, obj); + return 0; +} + +static int menu_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + add_menu_entry(obj, var->name, var->value); + return 0; +} + +int conf_load_config(int reload) +{ + if (!reload) { + if (aco_info_init(&cfg_info)) { + goto error; } - if (!strcasecmp(type, "bridge")) { - parse_bridge(cat, cfg); - } else if (!strcasecmp(type, "user")) { - parse_user(cat, cfg); - } else if (!strcasecmp(type, "menu")) { - parse_menu(cat, cfg); - } else { - continue; + if (ast_cli_register_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser))) { + goto error; } } - remove_all_delme(); - ast_config_destroy(cfg); - - return 0; + /* User options */ + aco_option_register(&cfg_info, "type", ACO_EXACT, user_types, NULL, OPT_NOOP_T, 0, 0); + aco_option_register(&cfg_info, "type", ACO_EXACT, bridge_types, NULL, OPT_NOOP_T, 0, 0); + aco_option_register(&cfg_info, "type", ACO_EXACT, menu_types, NULL, OPT_NOOP_T, 0, 0); + aco_option_register(&cfg_info, "admin", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ADMIN); + aco_option_register(&cfg_info, "marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_MARKEDUSER); + aco_option_register(&cfg_info, "startmuted", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_STARTMUTED); + aco_option_register(&cfg_info, "music_on_hold_when_empty", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_MUSICONHOLD); + aco_option_register(&cfg_info, "quiet", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_QUIET); + aco_option_register_custom(&cfg_info, "announce_user_count_all", ACO_EXACT, user_types, "no", announce_user_count_all_handler, 0); + aco_option_register(&cfg_info, "announce_user_count", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ANNOUNCEUSERCOUNT); + /* Negative logic. Defaults to "yes" and evaluates with ast_false(). If !ast_false(), USER_OPT_NOONLYPERSON is cleared */ + aco_option_register(&cfg_info, "announce_only_user", ACO_EXACT, user_types, "yes", OPT_BOOLFLAG_T, 0, FLDSET(struct user_profile, flags), USER_OPT_NOONLYPERSON); + aco_option_register(&cfg_info, "wait_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_WAITMARKED); + aco_option_register(&cfg_info, "end_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKED); + aco_option_register(&cfg_info, "talk_detection_events", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_TALKER_DETECT); + aco_option_register(&cfg_info, "dtmf_passthrough", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_DTMF_PASS); + aco_option_register(&cfg_info, "announce_join_leave", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ANNOUNCE_JOIN_LEAVE); + aco_option_register(&cfg_info, "pin", ACO_EXACT, user_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct user_profile, pin)); + aco_option_register(&cfg_info, "music_on_hold_class", ACO_EXACT, user_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct user_profile, moh_class)); + aco_option_register(&cfg_info, "announcement", ACO_EXACT, user_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct user_profile, announcement)); + aco_option_register(&cfg_info, "denoise", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_DENOISE); + aco_option_register(&cfg_info, "dsp_drop_silence", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_DROP_SILENCE); + aco_option_register(&cfg_info, "dsp_silence_threshold", ACO_EXACT, user_types, __stringify(DEFAULT_SILENCE_THRESHOLD), OPT_UINT_T, 0, FLDSET(struct user_profile, silence_threshold)); + aco_option_register(&cfg_info, "dsp_talking_threshold", ACO_EXACT, user_types, __stringify(DEFAULT_TALKING_THRESHOLD), OPT_UINT_T, 0, FLDSET(struct user_profile, silence_threshold)); + aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_JITTERBUFFER); + /* This option should only be used with the CONFBRIDGE dialplan function */ + aco_option_register_custom(&cfg_info, "template", ACO_EXACT, user_types, NULL, user_template_handler, 0); + + /* Bridge options */ + aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), USER_OPT_JITTERBUFFER); + /* "auto" will fail to parse as a uint, but we use PARSE_DEFAULT to set the value to 0 in that case, which is the value that auto resolves to */ + aco_option_register(&cfg_info, "internal_sample_rate", ACO_EXACT, bridge_types, "0", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct bridge_profile, internal_sample_rate), 0); + aco_option_register_custom(&cfg_info, "mixing_interval", ACO_EXACT, bridge_types, "20", mix_interval_handler, 0); + aco_option_register(&cfg_info, "record_conference", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), BRIDGE_OPT_RECORD_CONFERENCE); + aco_option_register_custom(&cfg_info, "video_mode", ACO_EXACT, bridge_types, NULL, video_mode_handler, 0); + aco_option_register(&cfg_info, "max_members", ACO_EXACT, bridge_types, "0", OPT_UINT_T, 0, FLDSET(struct bridge_profile, max_members)); + aco_option_register(&cfg_info, "record_file", ACO_EXACT, bridge_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, rec_file)); + aco_option_register_custom(&cfg_info, "^sound_", ACO_REGEX, bridge_types, NULL, sound_option_handler, 0); + /* This option should only be used with the CONFBRIDGE dialplan function */ + aco_option_register_custom(&cfg_info, "template", ACO_EXACT, bridge_types, NULL, bridge_template_handler, 0); + + /* Menu options */ + aco_option_register_custom(&cfg_info, "^[0-9A-D*#]+$", ACO_REGEX, menu_types, NULL, menu_option_handler, 0); + + return aco_process_config(&cfg_info, reload) == ACO_PROCESS_ERROR; +error: + conf_destroy_config(); + return -1; } static void conf_user_profile_copy(struct user_profile *dst, struct user_profile *src) @@ -1331,11 +1320,14 @@ static void conf_user_profile_copy(struct user_profile *dst, struct user_profile const struct user_profile *conf_find_user_profile(struct ast_channel *chan, const char *user_profile_name, struct user_profile *result) { - struct user_profile tmp; struct user_profile *tmp2; struct ast_datastore *datastore = NULL; struct func_confbridge_data *b_data = NULL; - ast_copy_string(tmp.name, user_profile_name, sizeof(tmp.name)); + RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup); + + if (!cfg) { + return NULL; + } if (chan) { ast_channel_lock(chan); @@ -1354,7 +1346,7 @@ const struct user_profile *conf_find_user_profile(struct ast_channel *chan, cons if (ast_strlen_zero(user_profile_name)) { user_profile_name = DEFAULT_USER_PROFILE; } - if (!(tmp2 = ao2_find(user_profiles, &tmp, OBJ_POINTER))) { + if (!(tmp2 = ao2_find(cfg->user_profiles, user_profile_name, OBJ_KEY))) { return NULL; } ao2_lock(tmp2); @@ -1383,10 +1375,10 @@ void conf_bridge_profile_destroy(struct bridge_profile *b_profile) const struct bridge_profile *conf_find_bridge_profile(struct ast_channel *chan, const char *bridge_profile_name, struct bridge_profile *result) { - struct bridge_profile tmp; struct bridge_profile *tmp2; struct ast_datastore *datastore = NULL; struct func_confbridge_data *b_data = NULL; + RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup); if (chan) { ast_channel_lock(chan); @@ -1404,8 +1396,7 @@ const struct bridge_profile *conf_find_bridge_profile(struct ast_channel *chan, if (ast_strlen_zero(bridge_profile_name)) { bridge_profile_name = DEFAULT_BRIDGE_PROFILE; } - ast_copy_string(tmp.name, bridge_profile_name, sizeof(tmp.name)); - if (!(tmp2 = ao2_find(bridge_profiles, &tmp, OBJ_POINTER))) { + if (!(tmp2 = ao2_find(cfg->bridge_profiles, bridge_profile_name, OBJ_KEY))) { return NULL; } ao2_lock(tmp2); @@ -1486,12 +1477,15 @@ int conf_find_menu_entry_by_sequence(const char *dtmf_sequence, struct conf_menu int conf_set_menu_to_user(const char *menu_name, struct conference_bridge_user *conference_bridge_user) { - struct conf_menu tmp; struct conf_menu *menu; struct conf_menu_entry *menu_entry = NULL; - ast_copy_string(tmp.name, menu_name, sizeof(tmp.name)); + RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup); + + if (!cfg) { + return -1; + } - if (!(menu = ao2_find(menus, &tmp, OBJ_POINTER))) { + if (!(menu = menu_find(cfg->menus, menu_name))) { return -1; } ao2_lock(menu); @@ -1520,3 +1514,10 @@ int conf_set_menu_to_user(const char *menu_name, struct conference_bridge_user * return 0; } + +void conf_destroy_config(void) +{ + ast_cli_unregister_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser)); + aco_info_destroy(&cfg_info); + ao2_global_obj_release(cfg_handle); +} diff --git a/apps/confbridge/include/confbridge.h b/apps/confbridge/include/confbridge.h index dd4ceff897..d3ead35a3e 100644 --- a/apps/confbridge/include/confbridge.h +++ b/apps/confbridge/include/confbridge.h @@ -120,7 +120,6 @@ struct conf_menu_entry { * sequences invoke.*/ struct conf_menu { char name[128]; - int delme; AST_LIST_HEAD_NOLOCK(, conf_menu_entry) entries; }; @@ -135,7 +134,6 @@ struct user_profile { unsigned int talking_threshold; /*! The time in ms of silence before a user is considered to be silent by the dsp. */ unsigned int silence_threshold; - int delme; }; enum conf_sounds { @@ -198,7 +196,6 @@ struct bridge_profile { unsigned int internal_sample_rate; /*!< The internal sample rate of the bridge. 0 when set to auto adjust mode. */ unsigned int mix_interval; /*!< The internal mixing interval used by the bridge. When set to 0 the bridgewill use a default interval. */ struct bridge_profile_sounds *sounds; - int delme; }; /*! \brief The structure that represents a conference bridge */ diff --git a/include/asterisk/config_options.h b/include/asterisk/config_options.h index 182740e02c..6de8d28cf7 100644 --- a/include/asterisk/config_options.h +++ b/include/asterisk/config_options.h @@ -281,6 +281,20 @@ enum aco_option_type { OPT_BOOLFLAG_T, + /*! \brief Type for default option handler for character arrays + * \note aco_option_register varargs: + * CHARFLDSET macro with a field of type char[] + * + * Example: + * {code} + * struct test_item { + * char description[128]; + * }; + * aco_option_register(&cfg_info, "description", ACO_EXACT, my_types, "none", OPT_CHAR_ARRAY_T, CHARFLDSET(struct test_item, description)); + * {endcode} + */ + OPT_CHAR_ARRAY_T, + /*! \brief Type for default option handler for codec preferences/capabilities * \note aco_option_register flags: * non-zero : This is an "allow" style option @@ -296,6 +310,7 @@ enum aco_option_type { * }; * aco_option_register(&cfg_info, "allow", ACO_EXACT, my_types, "ulaw,alaw", OPT_CODEC_T, 1, FLDSET(struct test_item, pref, cap)); * aco_option_register(&cfg_info, "disallow", ACO_EXACT, my_types, "all", OPT_CODEC_T, 0, FLDSET(struct test_item, pref, cap)); + * {endcode} */ OPT_CODEC_T, @@ -343,6 +358,13 @@ enum aco_option_type { */ OPT_INT_T, + /*! \brief Type for a default handler that should do nothing + * + * \note This might be useful for a "type" field that is valid, but doesn't + * actually need to do anything + */ + OPT_NOOP_T, + /*! \brief Type for default handler for ast_sockaddrs * * \note aco_option_register flags: @@ -373,7 +395,7 @@ enum aco_option_type { * AST_STRING_FIELD(thing); * ); * }; - * aco_option_register(&cfg_info, "thing", ACO_EXACT, my_types, NULL, OPT_STR_T, 0, STRFLDSET(struct test_item, thing)); + * aco_option_register(&cfg_info, "thing", ACO_EXACT, my_types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct test_item, thing)); * {endcode} */ OPT_STRINGFIELD_T, @@ -447,6 +469,21 @@ enum aco_process_status aco_process_config(struct aco_info *info, int reload); */ enum aco_process_status aco_process_ast_config(struct aco_info *info, struct aco_file *file, struct ast_config *cfg); +/*! \brief Parse a single ast_variable and apply it to an object + * \note This function can be used to build up an object by repeatedly passing in + * the config variable name and values that would be found in a config file. This can + * be useful if the object is to be populated by a dialplan function, for example. + * + * \param type The aco_type associated with the object + * \param cat The category to use + * \param var A variable to apply to the object + * \param obj A pointer to the object to be configured + * + * \retval 0 Success + * \retval -1 Failure + */ +int aco_process_var(struct aco_type *type, const char *cat, struct ast_variable *var, void *obj); + /*! \brief Parse each option defined in a config category * \param type The aco_type with the options for parsing * \param cfg The ast_config being parsed @@ -659,6 +696,14 @@ int aco_option_register_deprecated(struct aco_info *info, const char *name, stru */ #define STRFLDSET(type, ...) FLDSET(type, __VA_ARGS__, __field_mgr_pool, __field_mgr) +/*! \def CHARFLDSET(type, field) + * \brief A helper macro to pass the appropriate arguments to aco_option_register for OPT_CHAR_ARRAY_T + * \note This will pass the offset of the field and its length as arguments + * \param type The type with the char array field (e.g. "struct my_struct") + * \param field The name of char array field + */ +#define CHARFLDSET(type, field) ARGIFY(offsetof(type, field), sizeof(((type *)0)->field)) + /*! \def POPPED(...) * \brief A list of arguments without the first argument * \note Used internally to remove the leading "number of arguments" argument from ARGMAP for diff --git a/main/config_options.c b/main/config_options.c index 73fd6bf97b..a958342ec6 100644 --- a/main/config_options.c +++ b/main/config_options.c @@ -97,6 +97,8 @@ static int bool_handler_fn(const struct aco_option *opt, struct ast_variable *va static int boolflag_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj); static int acl_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj); static int codec_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj); +static int noop_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj); +static int chararray_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj); static aco_option_handler ast_config_option_default_handler(enum aco_option_type type) { @@ -104,9 +106,11 @@ static aco_option_handler ast_config_option_default_handler(enum aco_option_type case OPT_ACL_T: return acl_handler_fn; case OPT_BOOL_T: return bool_handler_fn; case OPT_BOOLFLAG_T: return boolflag_handler_fn; + case OPT_CHAR_ARRAY_T: return chararray_handler_fn; case OPT_CODEC_T: return codec_handler_fn; case OPT_DOUBLE_T: return double_handler_fn; case OPT_INT_T: return int_handler_fn; + case OPT_NOOP_T: return noop_handler_fn; case OPT_SOCKADDR_T: return sockaddr_handler_fn; case OPT_STRINGFIELD_T: return stringfield_handler_fn; case OPT_UINT_T: return uint_handler_fn; @@ -530,32 +534,40 @@ end: ao2_cleanup(info->internal->pending); return res; } +int aco_process_var(struct aco_type *type, const char *cat, struct ast_variable *var, void *obj) +{ + RAII_VAR(struct aco_option *, opt, aco_option_find(type, var->name), ao2_cleanup); + if (opt && opt->deprecated && !ast_strlen_zero(opt->aliased_to)) { + const char *alias = ast_strdupa(opt->aliased_to); + ast_log(LOG_WARNING, "At line %d of %s option '%s' is deprecated. Use '%s' instead\n", var->lineno, var->file, var->name, alias); + ao2_ref(opt, -1); + opt = aco_option_find(type, alias); + } + + if (!opt) { + ast_log(LOG_ERROR, "Could not find option suitable for category '%s' named '%s' at line %d of %s\n", cat, var->name, var->lineno, var->file); + return -1; + } + + if (!opt->handler) { + /* It should be impossible for an option to not have a handler */ + ast_log(LOG_ERROR, "BUG! Somehow a config option for %s/%s was created with no handler!\n", cat, var->name); + return -1; + } + if (opt->handler(opt, var, obj)) { + ast_log(LOG_ERROR, "Error parsing %s=%s at line %d of %s\n", var->name, var->value, var->lineno, var->file); + return -1; + } + + return 0; +} int aco_process_category_options(struct aco_type *type, struct ast_config *cfg, const char *cat, void *obj) { struct ast_variable *var; for (var = ast_variable_browse(cfg, cat); var; var = var->next) { - RAII_VAR(struct aco_option *, opt, aco_option_find(type, var->name), ao2_cleanup); - if (opt && opt->deprecated && !ast_strlen_zero(opt->aliased_to)) { - const char *alias = ast_strdupa(opt->aliased_to); - ast_log(LOG_WARNING, "At line %d of %s option '%s' is deprecated. Use '%s' instead\n", var->lineno, var->file, var->name, alias); - ao2_ref(opt, -1); - opt = aco_option_find(type, alias); - } - - if (!opt) { - ast_log(LOG_ERROR, "Could not find option suitable for category '%s' named '%s' at line %d of %s\n", cat, var->name, var->lineno, var->file); - return -1; - } - - if (!opt->handler) { - /* It should be impossible for an option to not have a handler */ - ast_log(LOG_ERROR, "BUG! Somehow a config option for %s/%s was created with no handler!\n", cat, var->name); - return -1; - } - if (opt->handler(opt, var, obj)) { - ast_log(LOG_ERROR, "Error parsing %s=%s at line %d of %s\n", var->name, var->value, var->lineno, var->file); + if (aco_process_var(type, cat, var, obj)) { return -1; } } @@ -813,3 +825,23 @@ static int sockaddr_handler_fn(const struct aco_option *opt, struct ast_variable struct ast_sockaddr *field = (struct ast_sockaddr *)(obj + opt->args[0]); return ast_parse_arg(var->value, PARSE_ADDR | opt->flags, field); } + +/*! \brief Default handler for doing noithing + */ +static int noop_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + return 0; +} + +/*! \brief Default handler for character arrays + * \note For a description of the opt->flags and opt->args values, see the documentation for + * enum aco_option_type in config_options.h + */ +static int chararray_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + char *field = (char *)(obj + opt->args[0]); + size_t len = opt->args[1]; + + ast_copy_string(field, var->value, len); + return 0; +}