From 2f674bcdd133056f9695eb63aa89c90c05bb1c55 Mon Sep 17 00:00:00 2001
From: Terry Wilson <twilson@digium.com>
Date: Thu, 19 Jul 2012 23:21:40 +0000
Subject: [PATCH] Convert app_confbridge to use the config options framework

Review: https://reviewboard.asterisk.org/r/2024/


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@370303 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 apps/confbridge/conf_config_parser.c | 835 ++++++++++++++-------------
 apps/confbridge/include/confbridge.h |   3 -
 include/asterisk/config_options.h    |  47 +-
 main/config_options.c                |  72 ++-
 4 files changed, 516 insertions(+), 441 deletions(-)

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;
+}