[asterisk-commits] twilson: trunk r370303 - in /trunk: apps/confbridge/ apps/confbridge/include/...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Jul 19 18:21:44 CDT 2012


Author: twilson
Date: Thu Jul 19 18:21:40 2012
New Revision: 370303

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=370303
Log:
Convert app_confbridge to use the config options framework

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

Modified:
    trunk/apps/confbridge/conf_config_parser.c
    trunk/apps/confbridge/include/confbridge.h
    trunk/include/asterisk/config_options.h
    trunk/main/config_options.c

Modified: trunk/apps/confbridge/conf_config_parser.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/confbridge/conf_config_parser.c?view=diff&rev=370303&r1=370302&r2=370303
==============================================================================
--- trunk/apps/confbridge/conf_config_parser.c (original)
+++ trunk/apps/confbridge/conf_config_parser.c Thu Jul 19 18:21:40 2012
@@ -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 @@
 #include "asterisk/stringfields.h"
 #include "asterisk/pbx.h"
 
-#define CONFBRIDGE_CONFIG "confbridge.conf"
-
-static struct ao2_container *user_profiles;
-static struct ao2_container *bridge_profiles;
-static struct ao2_container *menus;
+struct confbridge_cfg {
+	struct ao2_container *bridge_profiles;
+	struct ao2_container *user_profiles;
+	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);
+
+static void bridge_profile_destructor(void *obj)
+{
+	struct bridge_profile *b_profile = obj;
+	ao2_cleanup(b_profile->sounds);
+}
+
+static void *bridge_profile_alloc(const char *category)
+{
+	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 void *bridge_profile_find(struct ao2_container *container, const char *category)
+{
+	return ao2_find(container, category, OBJ_KEY);
+}
+
+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)
+{
+	return;
+}
+
+static void *user_profile_alloc(const char *category)
+{
+	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 void *user_profile_find(struct ao2_container *container, const char *category)
+{
+	return ao2_find(container, category, OBJ_KEY);
+}
+
+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 *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;
-	const struct bridge_profile *entry2 = arg;
-	return (!strcasecmp(entry1->name, entry2->name)) ? CMP_MATCH | CMP_STOP : 0;
+	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;
-	return ast_str_case_hash(b_profile->name);
-}
-static int bridge_mark_delme_cb(void *obj, void *arg, int flag)
-{
-	struct bridge_profile *entry = obj;
-	entry->delme = 1;
-	return 0;
-}
-static int match_bridge_delme_cb(void *obj, void *arg, int flag)
-{
-	const struct bridge_profile *entry = obj;
-	return entry->delme ? CMP_MATCH : 0;
+	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;
-	const struct conf_menu *entry2 = arg;
-	return (!strcasecmp(entry1->name, entry2->name)) ? CMP_MATCH | CMP_STOP : 0;
+	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 menu_hash_cb(const void *obj, const int flags)
 {
 	const struct conf_menu *menu = obj;
-	return ast_str_case_hash(menu->name);
-}
-static int menu_mark_delme_cb(void *obj, void *arg, int flag)
-{
-	struct conf_menu *entry = obj;
-	entry->delme = 1;
-	return 0;
-}
-static int match_menu_delme_cb(void *obj, void *arg, int flag)
-{
-	const struct conf_menu *entry = obj;
-	return entry->delme ? CMP_MATCH : 0;
+	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 @@
 /*! 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 @@
 	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 @@
 
 	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 @@
 	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 @@
 		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",
@@ -444,99 +404,6 @@
 		ast_datastore_free(datastore);
 	}
 	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)
@@ -743,38 +610,6 @@
 
 	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 @@
 	int wordlen = strlen(word);
 	struct ao2_iterator i;
 	struct user_profile *u_profile = NULL;
-
-	i = ao2_iterator_init(user_profiles, 0);
+	RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
+
+	if (!cfg) {
+		return NULL;
+	}
+
+	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 @@
 {
 	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 @@
 		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 @@
 	int wordlen = strlen(word);
 	struct ao2_iterator i;
 	struct bridge_profile *b_profile = NULL;
-
-	i = ao2_iterator_init(bridge_profiles, 0);
+	RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
+
+	if (!cfg) {
+		return NULL;
+	}
+
+	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 @@
 {
 	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 @@
 		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 @@
 	int wordlen = strlen(word);
 	struct ao2_iterator i;
 	struct conf_menu *menu = NULL;
-
-	i = ao2_iterator_init(menus, 0);
+	RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
+
+	if (!cfg) {
+		return NULL;
+	}
+
+	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 @@
 {
 	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 @@
 		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 @@
 		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 @@
 
 
 	ao2_unlock(menu);
-	ao2_ref(menu, -1);
 	return CLI_SUCCESS;
 }
 
@@ -1229,99 +1096,221 @@
 
 };
 
-static int conf_parse_init(void)
-{
-	if (!(user_profiles = ao2_container_alloc(283, user_hash_cb, user_cmp_cb))) {
-		conf_destroy_config();
-		return -1;
-	}
-
-	if (!(bridge_profiles = ao2_container_alloc(283, bridge_hash_cb, bridge_cmp_cb))) {
-		conf_destroy_config();
-		return -1;
-	}
-
-	if (!(menus = ao2_container_alloc(283, menu_hash_cb, menu_cmp_cb))) {
-		conf_destroy_config();
-		return -1;
-	}
-
-	ast_cli_register_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser));
-
+static void confbridge_cfg_destructor(void *obj)
+{
+	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 (!(cfg->user_profiles = ao2_container_alloc(283, user_hash_cb, user_cmp_cb))) {
+		goto error;
+	}
+
+	if (!(cfg->bridge_profiles = ao2_container_alloc(283, bridge_hash_cb, bridge_cmp_cb))) {
+		goto error;
+	}
+
+	if (!(cfg->menus = ao2_container_alloc(283, menu_hash_cb, menu_cmp_cb))) {
+		goto error;
+	}
+
+	return cfg;
+error:
+	ao2_ref(cfg, -1);
+	return NULL;
+}
+
+static int announce_user_count_all_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+	struct user_profile *u_profile = obj;
+
+	if (strcasecmp(var->name, "announce_user_count_all")) {
+		return -1;
+	}
+	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;
 }
 
-void conf_destroy_config()
-{
-	if (user_profiles) {
-		ao2_ref(user_profiles, -1);
-		user_profiles = NULL;
-	}
-	if (bridge_profiles) {
-		ao2_ref(bridge_profiles, -1);
-		bridge_profiles = NULL;
-	}
-
-	if (menus) {
-		ao2_ref(menus, -1);
-		menus = NULL;
-	}
-	ast_cli_unregister_multiple(cli_confbridge_parser, sizeof(cli_confbridge_parser) / sizeof(struct ast_cli_entry));
-}
-
-static void remove_all_delme(void)
-{
-	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);
-}
-
-static void mark_all_delme(void)
-{
-	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);
+static int mix_interval_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+	struct bridge_profile *b_profile = obj;
+
+	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;
+	}
+}
+
+static int video_mode_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+	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 int user_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+	struct user_profile *u_profile = obj;
+
+	return conf_find_user_profile(NULL, var->value, u_profile) ? 0 : -1;
+}
+
+static int bridge_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+	struct bridge_profile *b_profile = obj;
+	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, 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. */
+
+	return 0;
+}
+
+static int sound_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+	set_sound(var->name, var->value, obj);
+	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)
 {
-	struct ast_flags config_flags = { 0, };
-	struct ast_config *cfg = ast_config_load(CONFBRIDGE_CONFIG, config_flags);
-	const char *type = NULL;
-	char *cat = NULL;
-
 	if (!reload) {
-		conf_parse_init();
-	}
-
-	if (!cfg || cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
-		return 0;
-	}
-
-	mark_all_delme();
-
-	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;
-		}
-		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;
-		}
-	}
-
-	remove_all_delme();
-	ast_config_destroy(cfg);
-
-	return 0;
+		if (aco_info_init(&cfg_info)) {
+			goto error;
+		}
+		if (ast_cli_register_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser))) {
+			goto error;
+		}
+	}
+
+	/* 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 @@
 
 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;

[... 312 lines stripped ...]



More information about the asterisk-commits mailing list