2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2011, Digium, Inc.
6 * David Vossel <dvossel@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief ConfBridge config parser
23 * \author David Vossel <dvossel@digium.com>
27 <support_level>core</support_level>
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33 #include "asterisk/logger.h"
34 #include "asterisk/config.h"
35 #include "asterisk/config_options.h"
36 #include "include/confbridge.h"
37 #include "asterisk/astobj2.h"
38 #include "asterisk/cli.h"
39 #include "asterisk/bridging_features.h"
40 #include "asterisk/stringfields.h"
41 #include "asterisk/pbx.h"
43 struct confbridge_cfg {
44 struct ao2_container *bridge_profiles;
45 struct ao2_container *user_profiles;
46 struct ao2_container *menus;
49 static void *bridge_profile_alloc(const char *category);
50 static void *bridge_profile_find(struct ao2_container *container, const char *category);
51 static struct bridge_profile_sounds *bridge_profile_sounds_alloc(void);
53 static void bridge_profile_destructor(void *obj)
55 struct bridge_profile *b_profile = obj;
56 ao2_cleanup(b_profile->sounds);
59 static void *bridge_profile_alloc(const char *category)
61 struct bridge_profile *b_profile;
63 if (!(b_profile = ao2_alloc(sizeof(*b_profile), bridge_profile_destructor))) {
67 if (!(b_profile->sounds = bridge_profile_sounds_alloc())) {
68 ao2_ref(b_profile, -1);
72 ast_copy_string(b_profile->name, category, sizeof(b_profile->name));
77 static void *bridge_profile_find(struct ao2_container *container, const char *category)
79 return ao2_find(container, category, OBJ_KEY);
82 static struct aco_type bridge_type = {
84 .category_match = ACO_BLACKLIST,
85 .category = "^general$",
87 .matchvalue = "bridge",
88 .item_alloc = bridge_profile_alloc,
89 .item_find = bridge_profile_find,
90 .item_offset = offsetof(struct confbridge_cfg, bridge_profiles),
93 static void *user_profile_alloc(const char *category);
94 static void *user_profile_find(struct ao2_container *container, const char *category);
95 static void user_profile_destructor(void *obj)
100 static void *user_profile_alloc(const char *category)
102 struct user_profile *u_profile;
104 if (!(u_profile = ao2_alloc(sizeof(*u_profile), user_profile_destructor))) {
108 ast_copy_string(u_profile->name, category, sizeof(u_profile->name));
113 static void *user_profile_find(struct ao2_container *container, const char *category)
115 return ao2_find(container, category, OBJ_KEY);
118 static struct aco_type user_type = {
120 .category_match = ACO_BLACKLIST,
121 .category = "^general$",
122 .matchfield = "type",
123 .matchvalue = "user",
124 .item_alloc = user_profile_alloc,
125 .item_find = user_profile_find,
126 .item_offset = offsetof(struct confbridge_cfg, user_profiles),
129 static void *menu_alloc(const char *category);
130 static void *menu_find(struct ao2_container *container, const char *category);
131 static void menu_destructor(void *obj);
133 static void *menu_alloc(const char *category)
135 struct conf_menu *menu;
136 if (!(menu = ao2_alloc(sizeof(*menu), menu_destructor))) {
139 ast_copy_string(menu->name, category, sizeof(menu->name));
143 static void *menu_find(struct ao2_container *container, const char *category)
145 return ao2_find(container, category, OBJ_KEY);
148 static struct aco_type menu_type = {
150 .category_match = ACO_BLACKLIST,
151 .category = "^general$",
152 .matchfield = "type",
153 .matchvalue = "menu",
154 .item_alloc = menu_alloc,
155 .item_find = menu_find,
156 .item_offset = offsetof(struct confbridge_cfg, menus),
159 /* Used to pass to aco_option_register */
160 static struct aco_type *bridge_types[] = ACO_TYPES(&bridge_type);
161 static struct aco_type *menu_types[] = ACO_TYPES(&menu_type);
162 static struct aco_type *user_types[] = ACO_TYPES(&user_type);
164 /* The general category is reserved, but unused */
165 static struct aco_type general_type = {
167 .category_match = ACO_WHITELIST,
168 .category = "^general$",
171 static struct aco_file confbridge_conf = {
172 .filename = "confbridge.conf",
173 .types = ACO_TYPES(&bridge_type, &user_type, &menu_type, &general_type),
176 static AO2_GLOBAL_OBJ_STATIC(cfg_handle);
178 static void *confbridge_cfg_alloc(void);
180 CONFIG_INFO_STANDARD(cfg_info, cfg_handle, confbridge_cfg_alloc,
181 .files = ACO_FILES(&confbridge_conf),
184 /*! bridge profile container functions */
185 static int bridge_cmp_cb(void *obj, void *arg, int flags)
187 const struct bridge_profile *entry1 = obj, *entry2 = arg;
188 const char *name = arg;
189 return (!strcasecmp(entry1->name, flags & OBJ_KEY ? name : entry2->name)) ?
190 CMP_MATCH | CMP_STOP : 0;
192 static int bridge_hash_cb(const void *obj, const int flags)
194 const struct bridge_profile *b_profile = obj;
195 const char *name = obj;
196 return ast_str_case_hash(flags & OBJ_KEY ? name : b_profile->name);
199 /*! menu container functions */
200 static int menu_cmp_cb(void *obj, void *arg, int flags)
202 const struct conf_menu *entry1 = obj, *entry2 = arg;
203 const char *name = arg;
204 return (!strcasecmp(entry1->name, flags & OBJ_KEY ? name : entry2->name)) ?
205 CMP_MATCH | CMP_STOP : 0;
207 static int menu_hash_cb(const void *obj, const int flags)
209 const struct conf_menu *menu = obj;
210 const char *name = obj;
211 return ast_str_case_hash(flags & OBJ_KEY ? name : menu->name);
213 static void menu_destructor(void *obj)
215 struct conf_menu *menu = obj;
216 struct conf_menu_entry *entry = NULL;
218 while ((entry = AST_LIST_REMOVE_HEAD(&menu->entries, entry))) {
219 conf_menu_entry_destroy(entry);
224 /*! User profile container functions */
225 static int user_cmp_cb(void *obj, void *arg, int flags)
227 const struct user_profile *entry1 = obj, *entry2 = arg;
228 const char *name = arg;
229 return (!strcasecmp(entry1->name, flags & OBJ_KEY ? name : entry2->name)) ?
230 CMP_MATCH | CMP_STOP : 0;
232 static int user_hash_cb(const void *obj, const int flags)
234 const struct user_profile *u_profile = obj;
235 const char *name = obj;
236 return ast_str_case_hash(flags & OBJ_KEY ? name : u_profile->name);
239 /*! Bridge Profile Sounds functions */
240 static void bridge_profile_sounds_destroy_cb(void *obj)
242 struct bridge_profile_sounds *sounds = obj;
243 ast_string_field_free_memory(sounds);
246 static struct bridge_profile_sounds *bridge_profile_sounds_alloc(void)
248 struct bridge_profile_sounds *sounds = ao2_alloc(sizeof(*sounds), bridge_profile_sounds_destroy_cb);
253 if (ast_string_field_init(sounds, 512)) {
261 static int set_sound(const char *sound_name, const char *sound_file, struct bridge_profile *b_profile)
263 struct bridge_profile_sounds *sounds = b_profile->sounds;
264 if (ast_strlen_zero(sound_file)) {
268 if (!strcasecmp(sound_name, "sound_only_person")) {
269 ast_string_field_set(sounds, onlyperson, sound_file);
270 } else if (!strcasecmp(sound_name, "sound_only_one")) {
271 ast_string_field_set(sounds, onlyone, sound_file);
272 } else if (!strcasecmp(sound_name, "sound_has_joined")) {
273 ast_string_field_set(sounds, hasjoin, sound_file);
274 } else if (!strcasecmp(sound_name, "sound_has_left")) {
275 ast_string_field_set(sounds, hasleft, sound_file);
276 } else if (!strcasecmp(sound_name, "sound_kicked")) {
277 ast_string_field_set(sounds, kicked, sound_file);
278 } else if (!strcasecmp(sound_name, "sound_muted")) {
279 ast_string_field_set(sounds, muted, sound_file);
280 } else if (!strcasecmp(sound_name, "sound_unmuted")) {
281 ast_string_field_set(sounds, unmuted, sound_file);
282 } else if (!strcasecmp(sound_name, "sound_there_are")) {
283 ast_string_field_set(sounds, thereare, sound_file);
284 } else if (!strcasecmp(sound_name, "sound_other_in_party")) {
285 ast_string_field_set(sounds, otherinparty, sound_file);
286 } else if (!strcasecmp(sound_name, "sound_place_into_conference")) {
287 ast_string_field_set(sounds, placeintoconf, sound_file);
288 } else if (!strcasecmp(sound_name, "sound_wait_for_leader")) {
289 ast_string_field_set(sounds, waitforleader, sound_file);
290 } else if (!strcasecmp(sound_name, "sound_leader_has_left")) {
291 ast_string_field_set(sounds, leaderhasleft, sound_file);
292 } else if (!strcasecmp(sound_name, "sound_get_pin")) {
293 ast_string_field_set(sounds, getpin, sound_file);
294 } else if (!strcasecmp(sound_name, "sound_invalid_pin")) {
295 ast_string_field_set(sounds, invalidpin, sound_file);
296 } else if (!strcasecmp(sound_name, "sound_locked")) {
297 ast_string_field_set(sounds, locked, sound_file);
298 } else if (!strcasecmp(sound_name, "sound_unlocked_now")) {
299 ast_string_field_set(sounds, unlockednow, sound_file);
300 } else if (!strcasecmp(sound_name, "sound_locked_now")) {
301 ast_string_field_set(sounds, lockednow, sound_file);
302 } else if (!strcasecmp(sound_name, "sound_error_menu")) {
303 ast_string_field_set(sounds, errormenu, sound_file);
304 } else if (!strcasecmp(sound_name, "sound_join")) {
305 ast_string_field_set(sounds, join, sound_file);
306 } else if (!strcasecmp(sound_name, "sound_leave")) {
307 ast_string_field_set(sounds, leave, sound_file);
308 } else if (!strcasecmp(sound_name, "sound_participants_muted")) {
309 ast_string_field_set(sounds, participantsmuted, sound_file);
310 } else if (!strcasecmp(sound_name, "sound_participants_unmuted")) {
311 ast_string_field_set(sounds, participantsunmuted, sound_file);
319 /*! CONFBRIDGE dialplan function functions and channel datastore. */
320 struct func_confbridge_data {
321 struct bridge_profile b_profile;
322 struct user_profile u_profile;
323 unsigned int b_usable:1; /*!< Tells if bridge profile is usable or not */
324 unsigned int u_usable:1; /*!< Tells if user profile is usable or not */
326 static void func_confbridge_destroy_cb(void *data)
328 struct func_confbridge_data *b_data = data;
329 conf_bridge_profile_destroy(&b_data->b_profile);
332 static const struct ast_datastore_info confbridge_datastore = {
333 .type = "confbridge",
334 .destroy = func_confbridge_destroy_cb
336 int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value)
338 struct ast_datastore *datastore;
339 struct func_confbridge_data *b_data;
341 struct ast_variable tmpvar = { 0, };
342 AST_DECLARE_APP_ARGS(args,
347 /* parse all the required arguments and make sure they exist. */
348 if (ast_strlen_zero(data)) {
351 parse = ast_strdupa(data);
352 AST_STANDARD_APP_ARGS(args, parse);
353 if (ast_strlen_zero(args.type) || ast_strlen_zero(args.option)) {
357 ast_channel_lock(chan);
358 datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
360 datastore = ast_datastore_alloc(&confbridge_datastore, NULL);
362 ast_channel_unlock(chan);
365 b_data = ast_calloc(1, sizeof(*b_data));
367 ast_channel_unlock(chan);
368 ast_datastore_free(datastore);
371 b_data->b_profile.sounds = bridge_profile_sounds_alloc();
372 if (!b_data->b_profile.sounds) {
373 ast_channel_unlock(chan);
374 ast_datastore_free(datastore);
378 datastore->data = b_data;
379 ast_channel_datastore_add(chan, datastore);
381 b_data = datastore->data;
383 ast_channel_unlock(chan);
385 /* SET(CONFBRIDGE(type,option)=value) */
389 tmpvar.name = args.option;
390 tmpvar.value = value;
391 tmpvar.file = "CONFBRIDGE";
392 if (!strcasecmp(args.type, "bridge")) {
393 if (!aco_process_var(&bridge_type, "dialplan", &tmpvar, &b_data->b_profile)) {
394 b_data->b_usable = 1;
397 } else if (!strcasecmp(args.type, "user")) {
398 if (!aco_process_var(&user_type, "dialplan", &tmpvar, &b_data->u_profile)) {
399 b_data->u_usable = 1;
404 ast_log(LOG_WARNING, "%s(%s,%s) cannot be set to '%s'. Invalid type, option, or value.\n",
405 cmd, args.type, args.option, value);
409 static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum conf_menu_action_id id, char *databuf)
411 struct conf_menu_action *menu_action = ast_calloc(1, sizeof(*menu_action));
416 menu_action->id = id;
419 case MENU_ACTION_NOOP:
420 case MENU_ACTION_TOGGLE_MUTE:
421 case MENU_ACTION_INCREASE_LISTENING:
422 case MENU_ACTION_DECREASE_LISTENING:
423 case MENU_ACTION_INCREASE_TALKING:
424 case MENU_ACTION_DECREASE_TALKING:
425 case MENU_ACTION_RESET_LISTENING:
426 case MENU_ACTION_RESET_TALKING:
427 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
428 case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
429 case MENU_ACTION_PARTICIPANT_COUNT:
430 case MENU_ACTION_ADMIN_KICK_LAST:
431 case MENU_ACTION_LEAVE:
432 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
433 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
435 case MENU_ACTION_PLAYBACK:
436 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
437 if (!(ast_strlen_zero(databuf))) {
438 ast_copy_string(menu_action->data.playback_file, databuf, sizeof(menu_action->data.playback_file));
440 ast_free(menu_action);
444 case MENU_ACTION_DIALPLAN_EXEC:
445 if (!(ast_strlen_zero(databuf))) {
446 AST_DECLARE_APP_ARGS(args,
447 AST_APP_ARG(context);
449 AST_APP_ARG(priority);
451 AST_STANDARD_APP_ARGS(args, databuf);
452 if (!ast_strlen_zero(args.context)) {
453 ast_copy_string(menu_action->data.dialplan_args.context,
455 sizeof(menu_action->data.dialplan_args.context));
457 if (!ast_strlen_zero(args.exten)) {
458 ast_copy_string(menu_action->data.dialplan_args.exten,
460 sizeof(menu_action->data.dialplan_args.exten));
462 menu_action->data.dialplan_args.priority = 1; /* 1 by default */
463 if (!ast_strlen_zero(args.priority) &&
464 (sscanf(args.priority, "%30u", &menu_action->data.dialplan_args.priority) != 1)) {
465 /* invalid priority */
466 ast_free(menu_action);
470 ast_free(menu_action);
475 AST_LIST_INSERT_TAIL(&menu_entry->actions, menu_action, action);
480 static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *action_names)
482 struct conf_menu_entry *menu_entry = NULL, *cur = NULL;
484 char *tmp_action_names = ast_strdupa(action_names);
489 char *delimiter = ",";
491 if (!(menu_entry = ast_calloc(1, sizeof(*menu_entry)))) {
499 unsigned int action_len;
501 if (ast_strlen_zero(tmp_action_names)) {
504 startbrace = strchr(tmp_action_names, '(');
505 endbrace = strchr(tmp_action_names, ')');
506 comma = strchr(tmp_action_names, ',');
508 /* If the next action has brackets with comma delimited arguments in it,
509 * make the delimeter ')' instead of a comma to preserve the argments */
510 if (startbrace && endbrace && comma && (comma > startbrace && comma < endbrace)) {
516 if (!(action = strsep(&tmp_action_names, delimiter))) {
520 action = ast_strip(action);
521 if (ast_strlen_zero(action)) {
525 action_len = strlen(action);
526 ast_copy_string(menu_entry->dtmf, dtmf, sizeof(menu_entry->dtmf));
527 if (!strcasecmp(action, "toggle_mute")) {
528 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_TOGGLE_MUTE, NULL);
529 } else if (!strcasecmp(action, "no_op")) {
530 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_NOOP, NULL);
531 } else if (!strcasecmp(action, "increase_listening_volume")) {
532 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_LISTENING, NULL);
533 } else if (!strcasecmp(action, "decrease_listening_volume")) {
534 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_LISTENING, NULL);
535 } else if (!strcasecmp(action, "increase_talking_volume")) {
536 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_TALKING, NULL);
537 } else if (!strcasecmp(action, "reset_listening_volume")) {
538 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_LISTENING, NULL);
539 } else if (!strcasecmp(action, "reset_talking_volume")) {
540 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_TALKING, NULL);
541 } else if (!strcasecmp(action, "decrease_talking_volume")) {
542 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_TALKING, NULL);
543 } else if (!strcasecmp(action, "admin_toggle_conference_lock")) {
544 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_LOCK, NULL);
545 } else if (!strcasecmp(action, "admin_toggle_mute_participants")) {
546 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS, NULL);
547 } else if (!strcasecmp(action, "participant_count")) {
548 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PARTICIPANT_COUNT, NULL);
549 } else if (!strcasecmp(action, "admin_kick_last")) {
550 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_KICK_LAST, NULL);
551 } else if (!strcasecmp(action, "leave_conference")) {
552 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_LEAVE, NULL);
553 } else if (!strcasecmp(action, "set_as_single_video_src")) {
554 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_SET_SINGLE_VIDEO_SRC, NULL);
555 } else if (!strcasecmp(action, "release_as_single_video_src")) {
556 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC, NULL);
557 } else if (!strncasecmp(action, "dialplan_exec(", 14)) {
558 ast_copy_string(buf, action, sizeof(buf));
560 if ((action_args = strchr(action, '('))) {
563 /* it is possible that this argument may or may not
564 * have a closing brace at this point, it all depends on if
565 * comma delimited arguments were provided */
566 if ((tmp = strchr(action, ')'))) {
569 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DIALPLAN_EXEC, action_args);
570 } else if (action_len >= 21 && !strncasecmp(action, "playback_and_continue(", 22)) {
571 ast_copy_string(buf, action, sizeof(buf));
573 if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
577 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK_AND_CONTINUE, action_args);
578 } else if (action_len >= 8 && !strncasecmp(action, "playback(", 9)) {
579 ast_copy_string(buf, action, sizeof(buf));
581 if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
585 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK, action_args);
589 /* if adding any of the actions failed, bail */
591 struct conf_menu_action *menu_action;
592 while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
593 ast_free(menu_action);
595 ast_free(menu_entry);
599 /* remove any list entry with an identical DTMF sequence for overrides */
600 AST_LIST_TRAVERSE_SAFE_BEGIN(&menu->entries, cur, entry) {
601 if (!strcasecmp(cur->dtmf, menu_entry->dtmf)) {
602 AST_LIST_REMOVE_CURRENT(entry);
607 AST_LIST_TRAVERSE_SAFE_END;
609 AST_LIST_INSERT_TAIL(&menu->entries, menu_entry, entry);
614 static char *complete_user_profile_name(const char *line, const char *word, int pos, int state)
618 int wordlen = strlen(word);
619 struct ao2_iterator i;
620 struct user_profile *u_profile = NULL;
621 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
627 i = ao2_iterator_init(cfg->user_profiles, 0);
628 while ((u_profile = ao2_iterator_next(&i))) {
629 if (!strncasecmp(u_profile->name, word, wordlen) && ++which > state) {
630 res = ast_strdup(u_profile->name);
631 ao2_ref(u_profile, -1);
634 ao2_ref(u_profile, -1);
636 ao2_iterator_destroy(&i);
641 static char *handle_cli_confbridge_show_user_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
643 struct ao2_iterator it;
644 struct user_profile *u_profile;
645 RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
649 e->command = "confbridge show profile users";
651 "Usage confbridge show profile users\n";
657 if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
661 ast_cli(a->fd,"--------- User Profiles -----------\n");
662 ao2_lock(cfg->user_profiles);
663 it = ao2_iterator_init(cfg->user_profiles, 0);
664 while ((u_profile = ao2_iterator_next(&it))) {
665 ast_cli(a->fd,"%s\n", u_profile->name);
666 ao2_ref(u_profile, -1);
668 ao2_iterator_destroy(&it);
669 ao2_unlock(cfg->user_profiles);
673 static char *handle_cli_confbridge_show_user_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
675 struct user_profile u_profile;
679 e->command = "confbridge show profile user";
681 "Usage confbridge show profile user [<profile name>]\n";
685 return complete_user_profile_name(a->line, a->word, a->pos, a->n);
691 return CLI_SHOWUSAGE;
694 if (!(conf_find_user_profile(NULL, a->argv[4], &u_profile))) {
695 ast_cli(a->fd, "No conference user profile named '%s' found!\n", a->argv[4]);
699 ast_cli(a->fd,"--------------------------------------------\n");
700 ast_cli(a->fd,"Name: %s\n",
702 ast_cli(a->fd,"Admin: %s\n",
703 u_profile.flags & USER_OPT_ADMIN ?
705 ast_cli(a->fd,"Marked User: %s\n",
706 u_profile.flags & USER_OPT_MARKEDUSER ?
708 ast_cli(a->fd,"Start Muted: %s\n",
709 u_profile.flags & USER_OPT_STARTMUTED?
711 ast_cli(a->fd,"MOH When Empty: %s\n",
712 u_profile.flags & USER_OPT_MUSICONHOLD ?
713 "enabled" : "disabled");
714 ast_cli(a->fd,"MOH Class: %s\n",
715 ast_strlen_zero(u_profile.moh_class) ?
716 "default" : u_profile.moh_class);
717 ast_cli(a->fd,"Announcement: %s\n",
718 u_profile.announcement);
719 ast_cli(a->fd,"Quiet: %s\n",
720 u_profile.flags & USER_OPT_QUIET ?
721 "enabled" : "disabled");
722 ast_cli(a->fd,"Wait Marked: %s\n",
723 u_profile.flags & USER_OPT_WAITMARKED ?
724 "enabled" : "disabled");
725 ast_cli(a->fd,"END Marked: %s\n",
726 u_profile.flags & USER_OPT_ENDMARKED ?
727 "enabled" : "disabled");
728 ast_cli(a->fd,"Drop_silence: %s\n",
729 u_profile.flags & USER_OPT_DROP_SILENCE ?
730 "enabled" : "disabled");
731 ast_cli(a->fd,"Silence Threshold: %dms\n",
732 u_profile.silence_threshold);
733 ast_cli(a->fd,"Talking Threshold: %dms\n",
734 u_profile.talking_threshold);
735 ast_cli(a->fd,"Denoise: %s\n",
736 u_profile.flags & USER_OPT_DENOISE ?
737 "enabled" : "disabled");
738 ast_cli(a->fd,"Jitterbuffer: %s\n",
739 u_profile.flags & USER_OPT_JITTERBUFFER ?
740 "enabled" : "disabled");
741 ast_cli(a->fd,"Talk Detect Events: %s\n",
742 u_profile.flags & USER_OPT_TALKER_DETECT ?
743 "enabled" : "disabled");
744 ast_cli(a->fd,"DTMF Pass Through: %s\n",
745 u_profile.flags & USER_OPT_DTMF_PASS ?
746 "enabled" : "disabled");
747 ast_cli(a->fd,"PIN: %s\n",
748 ast_strlen_zero(u_profile.pin) ?
749 "None" : u_profile.pin);
750 ast_cli(a->fd,"Announce User Count: %s\n",
751 u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNT ?
752 "enabled" : "disabled");
753 ast_cli(a->fd,"Announce join/leave: %s\n",
754 u_profile.flags & USER_OPT_ANNOUNCE_JOIN_LEAVE ?
755 "enabled" : "disabled");
756 ast_cli(a->fd,"Announce User Count all: %s\n",
757 u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNTALL ?
758 "enabled" : "disabled");
764 static char *complete_bridge_profile_name(const char *line, const char *word, int pos, int state)
768 int wordlen = strlen(word);
769 struct ao2_iterator i;
770 struct bridge_profile *b_profile = NULL;
771 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
777 i = ao2_iterator_init(cfg->bridge_profiles, 0);
778 while ((b_profile = ao2_iterator_next(&i))) {
779 if (!strncasecmp(b_profile->name, word, wordlen) && ++which > state) {
780 res = ast_strdup(b_profile->name);
781 ao2_ref(b_profile, -1);
784 ao2_ref(b_profile, -1);
786 ao2_iterator_destroy(&i);
791 static char *handle_cli_confbridge_show_bridge_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
793 struct ao2_iterator it;
794 struct bridge_profile *b_profile;
795 RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
799 e->command = "confbridge show profile bridges";
801 "Usage confbridge show profile bridges\n";
807 if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
811 ast_cli(a->fd,"--------- Bridge Profiles -----------\n");
812 ao2_lock(cfg->bridge_profiles);
813 it = ao2_iterator_init(cfg->bridge_profiles, 0);
814 while ((b_profile = ao2_iterator_next(&it))) {
815 ast_cli(a->fd,"%s\n", b_profile->name);
816 ao2_ref(b_profile, -1);
818 ao2_iterator_destroy(&it);
819 ao2_unlock(cfg->bridge_profiles);
824 static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
826 struct bridge_profile b_profile;
831 e->command = "confbridge show profile bridge";
833 "Usage confbridge show profile bridge <profile name>\n";
837 return complete_bridge_profile_name(a->line, a->word, a->pos, a->n);
843 return CLI_SHOWUSAGE;
846 if (!(conf_find_bridge_profile(NULL, a->argv[4], &b_profile))) {
847 ast_cli(a->fd, "No conference bridge profile named '%s' found!\n", a->argv[4]);
851 ast_cli(a->fd,"--------------------------------------------\n");
852 ast_cli(a->fd,"Name: %s\n", b_profile.name);
854 if (b_profile.internal_sample_rate) {
855 snprintf(tmp, sizeof(tmp), "%d", b_profile.internal_sample_rate);
857 ast_copy_string(tmp, "auto", sizeof(tmp));
859 ast_cli(a->fd,"Internal Sample Rate: %s\n", tmp);
861 if (b_profile.mix_interval) {
862 ast_cli(a->fd,"Mixing Interval: %d\n", b_profile.mix_interval);
864 ast_cli(a->fd,"Mixing Interval: Default 20ms\n");
867 ast_cli(a->fd,"Record Conference: %s\n",
868 b_profile.flags & BRIDGE_OPT_RECORD_CONFERENCE ?
871 ast_cli(a->fd,"Record File: %s\n",
872 ast_strlen_zero(b_profile.rec_file) ? "Auto Generated" :
875 if (b_profile.max_members) {
876 ast_cli(a->fd,"Max Members: %d\n", b_profile.max_members);
878 ast_cli(a->fd,"Max Members: No Limit\n");
881 switch (b_profile.flags
882 & (BRIDGE_OPT_VIDEO_SRC_LAST_MARKED | BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
883 | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
884 case BRIDGE_OPT_VIDEO_SRC_LAST_MARKED:
885 ast_cli(a->fd, "Video Mode: last_marked\n");
887 case BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED:
888 ast_cli(a->fd, "Video Mode: first_marked\n");
890 case BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER:
891 ast_cli(a->fd, "Video Mode: follow_talker\n");
894 ast_cli(a->fd, "Video Mode: no video\n");
897 /* Opps. We have more than one video mode flag set. */
902 ast_cli(a->fd,"sound_join: %s\n", conf_get_sound(CONF_SOUND_JOIN, b_profile.sounds));
903 ast_cli(a->fd,"sound_leave: %s\n", conf_get_sound(CONF_SOUND_LEAVE, b_profile.sounds));
904 ast_cli(a->fd,"sound_only_person: %s\n", conf_get_sound(CONF_SOUND_ONLY_PERSON, b_profile.sounds));
905 ast_cli(a->fd,"sound_has_joined: %s\n", conf_get_sound(CONF_SOUND_HAS_JOINED, b_profile.sounds));
906 ast_cli(a->fd,"sound_has_left: %s\n", conf_get_sound(CONF_SOUND_HAS_LEFT, b_profile.sounds));
907 ast_cli(a->fd,"sound_kicked: %s\n", conf_get_sound(CONF_SOUND_KICKED, b_profile.sounds));
908 ast_cli(a->fd,"sound_muted: %s\n", conf_get_sound(CONF_SOUND_MUTED, b_profile.sounds));
909 ast_cli(a->fd,"sound_unmuted: %s\n", conf_get_sound(CONF_SOUND_UNMUTED, b_profile.sounds));
910 ast_cli(a->fd,"sound_there_are: %s\n", conf_get_sound(CONF_SOUND_THERE_ARE, b_profile.sounds));
911 ast_cli(a->fd,"sound_other_in_party: %s\n", conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, b_profile.sounds));
912 ast_cli(a->fd,"sound_place_into_conference: %s\n", conf_get_sound(CONF_SOUND_PLACE_IN_CONF, b_profile.sounds));
913 ast_cli(a->fd,"sound_wait_for_leader: %s\n", conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, b_profile.sounds));
914 ast_cli(a->fd,"sound_leader_has_left: %s\n", conf_get_sound(CONF_SOUND_LEADER_HAS_LEFT, b_profile.sounds));
915 ast_cli(a->fd,"sound_get_pin: %s\n", conf_get_sound(CONF_SOUND_GET_PIN, b_profile.sounds));
916 ast_cli(a->fd,"sound_invalid_pin: %s\n", conf_get_sound(CONF_SOUND_INVALID_PIN, b_profile.sounds));
917 ast_cli(a->fd,"sound_locked: %s\n", conf_get_sound(CONF_SOUND_LOCKED, b_profile.sounds));
918 ast_cli(a->fd,"sound_unlocked_now: %s\n", conf_get_sound(CONF_SOUND_UNLOCKED_NOW, b_profile.sounds));
919 ast_cli(a->fd,"sound_lockednow: %s\n", conf_get_sound(CONF_SOUND_LOCKED_NOW, b_profile.sounds));
920 ast_cli(a->fd,"sound_error_menu: %s\n", conf_get_sound(CONF_SOUND_ERROR_MENU, b_profile.sounds));
921 ast_cli(a->fd,"sound_participants_muted: %s\n", conf_get_sound(CONF_SOUND_PARTICIPANTS_MUTED, b_profile.sounds));
922 ast_cli(a->fd,"sound_participants_unmuted: %s\n", conf_get_sound(CONF_SOUND_PARTICIPANTS_UNMUTED, b_profile.sounds));
925 conf_bridge_profile_destroy(&b_profile);
929 static char *complete_menu_name(const char *line, const char *word, int pos, int state)
933 int wordlen = strlen(word);
934 struct ao2_iterator i;
935 struct conf_menu *menu = NULL;
936 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
942 i = ao2_iterator_init(cfg->menus, 0);
943 while ((menu = ao2_iterator_next(&i))) {
944 if (!strncasecmp(menu->name, word, wordlen) && ++which > state) {
945 res = ast_strdup(menu->name);
951 ao2_iterator_destroy(&i);
956 static char *handle_cli_confbridge_show_menus(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
958 struct ao2_iterator it;
959 struct conf_menu *menu;
960 RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
964 e->command = "confbridge show menus";
966 "Usage confbridge show profile menus\n";
972 if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
976 ast_cli(a->fd,"--------- Menus -----------\n");
977 ao2_lock(cfg->menus);
978 it = ao2_iterator_init(cfg->menus, 0);
979 while ((menu = ao2_iterator_next(&it))) {
980 ast_cli(a->fd,"%s\n", menu->name);
983 ao2_iterator_destroy(&it);
984 ao2_unlock(cfg->menus);
989 static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
991 RAII_VAR(struct conf_menu *, menu, NULL, ao2_cleanup);
992 RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
993 struct conf_menu_entry *menu_entry = NULL;
994 struct conf_menu_action *menu_action = NULL;
998 e->command = "confbridge show menu";
1000 "Usage confbridge show menu [<menu name>]\n";
1004 return complete_menu_name(a->line, a->word, a->pos, a->n);
1010 return CLI_SHOWUSAGE;
1013 if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
1017 if (!(menu = menu_find(cfg->menus, a->argv[3]))) {
1018 ast_cli(a->fd, "No conference menu named '%s' found!\n", a->argv[3]);
1023 ast_cli(a->fd,"Name: %s\n", menu->name);
1024 AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
1026 ast_cli(a->fd, "%s=", menu_entry->dtmf);
1027 AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
1029 ast_cli(a->fd, ", ");
1031 switch (menu_action->id) {
1032 case MENU_ACTION_TOGGLE_MUTE:
1033 ast_cli(a->fd, "toggle_mute");
1035 case MENU_ACTION_NOOP:
1036 ast_cli(a->fd, "no_op");
1038 case MENU_ACTION_INCREASE_LISTENING:
1039 ast_cli(a->fd, "increase_listening_volume");
1041 case MENU_ACTION_DECREASE_LISTENING:
1042 ast_cli(a->fd, "decrease_listening_volume");
1044 case MENU_ACTION_RESET_LISTENING:
1045 ast_cli(a->fd, "reset_listening_volume");
1047 case MENU_ACTION_RESET_TALKING:
1048 ast_cli(a->fd, "reset_talking_volume");
1050 case MENU_ACTION_INCREASE_TALKING:
1051 ast_cli(a->fd, "increase_talking_volume");
1053 case MENU_ACTION_DECREASE_TALKING:
1054 ast_cli(a->fd, "decrease_talking_volume");
1056 case MENU_ACTION_PLAYBACK:
1057 ast_cli(a->fd, "playback(%s)", menu_action->data.playback_file);
1059 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
1060 ast_cli(a->fd, "playback_and_continue(%s)", menu_action->data.playback_file);
1062 case MENU_ACTION_DIALPLAN_EXEC:
1063 ast_cli(a->fd, "dialplan_exec(%s,%s,%d)",
1064 menu_action->data.dialplan_args.context,
1065 menu_action->data.dialplan_args.exten,
1066 menu_action->data.dialplan_args.priority);
1068 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
1069 ast_cli(a->fd, "admin_toggle_conference_lock");
1071 case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
1072 ast_cli(a->fd, "admin_toggle_mute_participants");
1074 case MENU_ACTION_PARTICIPANT_COUNT:
1075 ast_cli(a->fd, "participant_count");
1077 case MENU_ACTION_ADMIN_KICK_LAST:
1078 ast_cli(a->fd, "admin_kick_last");
1080 case MENU_ACTION_LEAVE:
1081 ast_cli(a->fd, "leave_conference");
1083 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
1084 ast_cli(a->fd, "set_as_single_video_src");
1086 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
1087 ast_cli(a->fd, "release_as_single_video_src");
1092 ast_cli(a->fd,"\n");
1100 static struct ast_cli_entry cli_confbridge_parser[] = {
1101 AST_CLI_DEFINE(handle_cli_confbridge_show_user_profile, "Show a conference user profile."),
1102 AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profile, "Show a conference bridge profile."),
1103 AST_CLI_DEFINE(handle_cli_confbridge_show_menu, "Show a conference menu"),
1104 AST_CLI_DEFINE(handle_cli_confbridge_show_user_profiles, "Show a list of conference user profiles."),
1105 AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profiles, "Show a list of conference bridge profiles."),
1106 AST_CLI_DEFINE(handle_cli_confbridge_show_menus, "Show a list of conference menus"),
1110 static void confbridge_cfg_destructor(void *obj)
1112 struct confbridge_cfg *cfg = obj;
1113 ao2_cleanup(cfg->user_profiles);
1114 ao2_cleanup(cfg->bridge_profiles);
1115 ao2_cleanup(cfg->menus);
1118 void *confbridge_cfg_alloc(void)
1120 struct confbridge_cfg *cfg;
1122 if (!(cfg = ao2_alloc(sizeof(*cfg), confbridge_cfg_destructor))) {
1126 if (!(cfg->user_profiles = ao2_container_alloc(283, user_hash_cb, user_cmp_cb))) {
1130 if (!(cfg->bridge_profiles = ao2_container_alloc(283, bridge_hash_cb, bridge_cmp_cb))) {
1134 if (!(cfg->menus = ao2_container_alloc(283, menu_hash_cb, menu_cmp_cb))) {
1144 static int announce_user_count_all_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1146 struct user_profile *u_profile = obj;
1148 if (strcasecmp(var->name, "announce_user_count_all")) {
1151 if (ast_true(var->value)) {
1152 u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL;
1153 } else if (ast_false(var->value)) {
1154 u_profile->flags = u_profile->flags & ~USER_OPT_ANNOUNCEUSERCOUNTALL;
1155 } else if (sscanf(var->value, "%30u", &u_profile->announce_user_count_all_after) == 1) {
1156 u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL;
1163 static int mix_interval_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1165 struct bridge_profile *b_profile = obj;
1167 if (strcasecmp(var->name, "mixing_interval")) {
1170 if (sscanf(var->value, "%30u", &b_profile->mix_interval) != 1) {
1173 switch (b_profile->mix_interval) {
1184 static int video_mode_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1186 struct bridge_profile *b_profile = obj;
1188 if (strcasecmp(var->name, "video_mode")) {
1191 if (!strcasecmp(var->value, "first_marked")) {
1192 ast_set_flags_to(b_profile,
1193 BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
1194 | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
1195 | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER,
1196 BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED);
1197 } else if (!strcasecmp(var->value, "last_marked")) {
1198 ast_set_flags_to(b_profile,
1199 BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
1200 | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
1201 | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER,
1202 BRIDGE_OPT_VIDEO_SRC_LAST_MARKED);
1203 } else if (!strcasecmp(var->value, "follow_talker")) {
1204 ast_set_flags_to(b_profile,
1205 BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
1206 | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
1207 | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER,
1208 BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
1209 } else if (!strcasecmp(var->value, "none")) {
1210 ast_clear_flag(b_profile,
1211 BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
1212 | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
1213 | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
1220 static int user_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1222 struct user_profile *u_profile = obj;
1224 return conf_find_user_profile(NULL, var->value, u_profile) ? 0 : -1;
1227 static int bridge_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1229 struct bridge_profile *b_profile = obj;
1230 struct bridge_profile_sounds *sounds = bridge_profile_sounds_alloc();
1231 struct bridge_profile_sounds *oldsounds = b_profile->sounds;
1236 if (!(conf_find_bridge_profile(NULL, var->value, b_profile))) {
1237 ao2_ref(sounds, -1);
1240 /* Using a bridge profile as a template is a little complicated due to the sounds. Since the sounds
1241 * structure of a dynamic profile will need to be altered, a completely new sounds structure must be
1242 * created instead of simply holding a reference to the one built by the config file. */
1243 ast_string_field_set(sounds, onlyperson, b_profile->sounds->onlyperson);
1244 ast_string_field_set(sounds, hasjoin, b_profile->sounds->hasjoin);
1245 ast_string_field_set(sounds, hasleft, b_profile->sounds->hasleft);
1246 ast_string_field_set(sounds, kicked, b_profile->sounds->kicked);
1247 ast_string_field_set(sounds, muted, b_profile->sounds->muted);
1248 ast_string_field_set(sounds, unmuted, b_profile->sounds->unmuted);
1249 ast_string_field_set(sounds, thereare, b_profile->sounds->thereare);
1250 ast_string_field_set(sounds, otherinparty, b_profile->sounds->otherinparty);
1251 ast_string_field_set(sounds, placeintoconf, b_profile->sounds->placeintoconf);
1252 ast_string_field_set(sounds, waitforleader, b_profile->sounds->waitforleader);
1253 ast_string_field_set(sounds, leaderhasleft, b_profile->sounds->leaderhasleft);
1254 ast_string_field_set(sounds, getpin, b_profile->sounds->getpin);
1255 ast_string_field_set(sounds, invalidpin, b_profile->sounds->invalidpin);
1256 ast_string_field_set(sounds, locked, b_profile->sounds->locked);
1257 ast_string_field_set(sounds, unlockednow, b_profile->sounds->unlockednow);
1258 ast_string_field_set(sounds, lockednow, b_profile->sounds->lockednow);
1259 ast_string_field_set(sounds, errormenu, b_profile->sounds->errormenu);
1260 ast_string_field_set(sounds, participantsmuted, b_profile->sounds->participantsmuted);
1261 ast_string_field_set(sounds, participantsunmuted, b_profile->sounds->participantsunmuted);
1263 ao2_ref(b_profile->sounds, -1); /* sounds struct copied over to it from the template by reference only. */
1264 ao2_ref(oldsounds, -1); /* original sounds struct we don't need anymore */
1265 b_profile->sounds = sounds; /* the new sounds struct that is a deep copy of the one from the template. */
1270 static int sound_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1272 set_sound(var->name, var->value, obj);
1276 static int menu_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1278 add_menu_entry(obj, var->name, var->value);
1282 int conf_load_config(int reload)
1285 if (aco_info_init(&cfg_info)) {
1291 aco_option_register(&cfg_info, "type", ACO_EXACT, user_types, NULL, OPT_NOOP_T, 0, 0);
1292 aco_option_register(&cfg_info, "type", ACO_EXACT, bridge_types, NULL, OPT_NOOP_T, 0, 0);
1293 aco_option_register(&cfg_info, "type", ACO_EXACT, menu_types, NULL, OPT_NOOP_T, 0, 0);
1294 aco_option_register(&cfg_info, "admin", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ADMIN);
1295 aco_option_register(&cfg_info, "marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_MARKEDUSER);
1296 aco_option_register(&cfg_info, "startmuted", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_STARTMUTED);
1297 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);
1298 aco_option_register(&cfg_info, "quiet", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_QUIET);
1299 aco_option_register_custom(&cfg_info, "announce_user_count_all", ACO_EXACT, user_types, "no", announce_user_count_all_handler, 0);
1300 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);
1301 /* Negative logic. Defaults to "yes" and evaluates with ast_false(). If !ast_false(), USER_OPT_NOONLYPERSON is cleared */
1302 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);
1303 aco_option_register(&cfg_info, "wait_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_WAITMARKED);
1304 aco_option_register(&cfg_info, "end_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKED);
1305 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);
1306 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);
1307 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);
1308 aco_option_register(&cfg_info, "pin", ACO_EXACT, user_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct user_profile, pin));
1309 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));
1310 aco_option_register(&cfg_info, "announcement", ACO_EXACT, user_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct user_profile, announcement));
1311 aco_option_register(&cfg_info, "denoise", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_DENOISE);
1312 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);
1313 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));
1314 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));
1315 aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_JITTERBUFFER);
1316 /* This option should only be used with the CONFBRIDGE dialplan function */
1317 aco_option_register_custom(&cfg_info, "template", ACO_EXACT, user_types, NULL, user_template_handler, 0);
1319 /* Bridge options */
1320 aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), USER_OPT_JITTERBUFFER);
1321 /* "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 */
1322 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);
1323 aco_option_register_custom(&cfg_info, "mixing_interval", ACO_EXACT, bridge_types, "20", mix_interval_handler, 0);
1324 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);
1325 aco_option_register_custom(&cfg_info, "video_mode", ACO_EXACT, bridge_types, NULL, video_mode_handler, 0);
1326 aco_option_register(&cfg_info, "max_members", ACO_EXACT, bridge_types, "0", OPT_UINT_T, 0, FLDSET(struct bridge_profile, max_members));
1327 aco_option_register(&cfg_info, "record_file", ACO_EXACT, bridge_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, rec_file));
1328 aco_option_register_custom(&cfg_info, "^sound_", ACO_REGEX, bridge_types, NULL, sound_option_handler, 0);
1329 /* This option should only be used with the CONFBRIDGE dialplan function */
1330 aco_option_register_custom(&cfg_info, "template", ACO_EXACT, bridge_types, NULL, bridge_template_handler, 0);
1333 aco_option_register_custom(&cfg_info, "^[0-9A-D*#]+$", ACO_REGEX, menu_types, NULL, menu_option_handler, 0);
1335 if (aco_process_config(&cfg_info, reload) == ACO_PROCESS_ERROR) {
1339 if (!reload && ast_cli_register_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser))) {
1345 /* On a reload, just keep the config we already have in place. */
1347 conf_destroy_config();
1352 static void conf_user_profile_copy(struct user_profile *dst, struct user_profile *src)
1354 memcpy(dst, src, sizeof(*dst));
1357 const struct user_profile *conf_find_user_profile(struct ast_channel *chan, const char *user_profile_name, struct user_profile *result)
1359 struct user_profile *tmp2;
1360 struct ast_datastore *datastore = NULL;
1361 struct func_confbridge_data *b_data = NULL;
1362 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
1369 ast_channel_lock(chan);
1370 datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
1371 ast_channel_unlock(chan);
1373 b_data = datastore->data;
1374 if (b_data->u_usable) {
1375 conf_user_profile_copy(result, &b_data->u_profile);
1381 if (ast_strlen_zero(user_profile_name)) {
1382 user_profile_name = DEFAULT_USER_PROFILE;
1384 if (!(tmp2 = ao2_find(cfg->user_profiles, user_profile_name, OBJ_KEY))) {
1388 conf_user_profile_copy(result, tmp2);
1395 void conf_bridge_profile_copy(struct bridge_profile *dst, struct bridge_profile *src)
1397 memcpy(dst, src, sizeof(*dst));
1399 ao2_ref(src->sounds, +1);
1403 void conf_bridge_profile_destroy(struct bridge_profile *b_profile)
1405 if (b_profile->sounds) {
1406 ao2_ref(b_profile->sounds, -1);
1407 b_profile->sounds = NULL;
1411 const struct bridge_profile *conf_find_bridge_profile(struct ast_channel *chan, const char *bridge_profile_name, struct bridge_profile *result)
1413 struct bridge_profile *tmp2;
1414 struct ast_datastore *datastore = NULL;
1415 struct func_confbridge_data *b_data = NULL;
1416 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
1423 ast_channel_lock(chan);
1424 datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
1425 ast_channel_unlock(chan);
1427 b_data = datastore->data;
1428 if (b_data->b_usable) {
1429 conf_bridge_profile_copy(result, &b_data->b_profile);
1434 if (ast_strlen_zero(bridge_profile_name)) {
1435 bridge_profile_name = DEFAULT_BRIDGE_PROFILE;
1437 if (!(tmp2 = ao2_find(cfg->bridge_profiles, bridge_profile_name, OBJ_KEY))) {
1441 conf_bridge_profile_copy(result, tmp2);
1448 struct dtmf_menu_hook_pvt {
1449 struct conference_bridge_user *conference_bridge_user;
1450 struct conf_menu_entry menu_entry;
1451 struct conf_menu *menu;
1454 static void menu_hook_destroy(void *hook_pvt)
1456 struct dtmf_menu_hook_pvt *pvt = hook_pvt;
1457 struct conf_menu_action *action = NULL;
1459 ao2_ref(pvt->menu, -1);
1461 while ((action = AST_LIST_REMOVE_HEAD(&pvt->menu_entry.actions, action))) {
1467 static int menu_hook_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1469 struct dtmf_menu_hook_pvt *pvt = hook_pvt;
1470 return conf_handle_dtmf(bridge_channel, pvt->conference_bridge_user, &pvt->menu_entry, pvt->menu);
1473 static int copy_menu_entry(struct conf_menu_entry *dst, struct conf_menu_entry *src)
1475 struct conf_menu_action *menu_action = NULL;
1476 struct conf_menu_action *new_menu_action = NULL;
1478 memcpy(dst, src, sizeof(*dst));
1479 AST_LIST_HEAD_INIT_NOLOCK(&dst->actions);
1481 AST_LIST_TRAVERSE(&src->actions, menu_action, action) {
1482 if (!(new_menu_action = ast_calloc(1, sizeof(*new_menu_action)))) {
1485 memcpy(new_menu_action, menu_action, sizeof(*new_menu_action));
1486 AST_LIST_INSERT_HEAD(&dst->actions, new_menu_action, action);
1491 void conf_menu_entry_destroy(struct conf_menu_entry *menu_entry)
1493 struct conf_menu_action *menu_action = NULL;
1494 while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
1495 ast_free(menu_action);
1499 int conf_find_menu_entry_by_sequence(const char *dtmf_sequence, struct conf_menu *menu, struct conf_menu_entry *result)
1501 struct conf_menu_entry *menu_entry = NULL;
1504 AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
1505 if (!strcasecmp(menu_entry->dtmf, dtmf_sequence)) {
1506 copy_menu_entry(result, menu_entry);
1516 int conf_set_menu_to_user(const char *menu_name, struct conference_bridge_user *conference_bridge_user)
1518 struct conf_menu *menu;
1519 struct conf_menu_entry *menu_entry = NULL;
1520 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
1526 if (!(menu = menu_find(cfg->menus, menu_name))) {
1530 AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
1531 struct dtmf_menu_hook_pvt *pvt;
1532 if (!(pvt = ast_calloc(1, sizeof(*pvt)))) {
1537 if (copy_menu_entry(&pvt->menu_entry, menu_entry)) {
1543 pvt->conference_bridge_user = conference_bridge_user;
1547 ast_bridge_features_hook(&conference_bridge_user->features, pvt->menu_entry.dtmf, menu_hook_callback, pvt, menu_hook_destroy);
1556 void conf_destroy_config(void)
1558 ast_cli_unregister_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser));
1559 aco_info_destroy(&cfg_info);
1560 ao2_global_obj_release(cfg_handle);