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>
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29 #include "asterisk/logger.h"
30 #include "asterisk/config.h"
31 #include "include/confbridge.h"
32 #include "asterisk/astobj2.h"
33 #include "asterisk/cli.h"
34 #include "asterisk/bridging_features.h"
35 #include "asterisk/stringfields.h"
36 #include "asterisk/pbx.h"
38 #define CONFBRIDGE_CONFIG "confbridge.conf"
40 static struct ao2_container *user_profiles;
41 static struct ao2_container *bridge_profiles;
42 static struct ao2_container *menus;
44 /*! bridge profile container functions */
45 static int bridge_cmp_cb(void *obj, void *arg, int flags)
47 const struct bridge_profile *entry1 = obj;
48 const struct bridge_profile *entry2 = arg;
49 return (!strcasecmp(entry1->name, entry2->name)) ? CMP_MATCH | CMP_STOP : 0;
51 static int bridge_hash_cb(const void *obj, const int flags)
53 const struct bridge_profile *b_profile = obj;
54 return ast_str_case_hash(b_profile->name);
56 static int bridge_mark_delme_cb(void *obj, void *arg, int flag)
58 struct bridge_profile *entry = obj;
62 static int match_bridge_delme_cb(void *obj, void *arg, int flag)
64 const struct bridge_profile *entry = obj;
65 return entry->delme ? CMP_MATCH : 0;
68 /*! menu container functions */
69 static int menu_cmp_cb(void *obj, void *arg, int flags)
71 const struct conf_menu *entry1 = obj;
72 const struct conf_menu *entry2 = arg;
73 return (!strcasecmp(entry1->name, entry2->name)) ? CMP_MATCH | CMP_STOP : 0;
75 static int menu_hash_cb(const void *obj, const int flags)
77 const struct conf_menu *menu = obj;
78 return ast_str_case_hash(menu->name);
80 static int menu_mark_delme_cb(void *obj, void *arg, int flag)
82 struct conf_menu *entry = obj;
86 static int match_menu_delme_cb(void *obj, void *arg, int flag)
88 const struct conf_menu *entry = obj;
89 return entry->delme ? CMP_MATCH : 0;
91 static void menu_destructor(void *obj)
93 struct conf_menu *menu = obj;
94 struct conf_menu_entry *entry = NULL;
96 while ((entry = AST_LIST_REMOVE_HEAD(&menu->entries, entry))) {
97 conf_menu_entry_destroy(entry);
102 /*! User profile container functions */
103 static int user_cmp_cb(void *obj, void *arg, int flags)
105 const struct user_profile *entry1 = obj;
106 const struct user_profile *entry2 = arg;
107 return (!strcasecmp(entry1->name, entry2->name)) ? CMP_MATCH | CMP_STOP : 0;
109 static int user_hash_cb(const void *obj, const int flags)
111 const struct user_profile *u_profile = obj;
112 return ast_str_case_hash(u_profile->name);
114 static int user_mark_delme_cb(void *obj, void *arg, int flag)
116 struct user_profile *entry = obj;
120 static int match_user_delme_cb(void *obj, void *arg, int flag)
122 const struct user_profile *entry = obj;
123 return entry->delme ? CMP_MATCH : 0;
126 /*! Bridge Profile Sounds functions */
127 static void bridge_profile_sounds_destroy_cb(void *obj)
129 struct bridge_profile_sounds *sounds = obj;
130 ast_string_field_free_memory(sounds);
133 static struct bridge_profile_sounds *bridge_profile_sounds_alloc(void)
135 struct bridge_profile_sounds *sounds = ao2_alloc(sizeof(*sounds), bridge_profile_sounds_destroy_cb);
140 if (ast_string_field_init(sounds, 512)) {
148 static int set_user_option(const char *name, const char *value, struct user_profile *u_profile)
150 if (!strcasecmp(name, "admin")) {
151 ast_set2_flag(u_profile, ast_true(value), USER_OPT_ADMIN);
152 } else if (!strcasecmp(name, "marked")) {
153 ast_set2_flag(u_profile, ast_true(value), USER_OPT_MARKEDUSER);
154 } else if (!strcasecmp(name, "startmuted")) {
155 ast_set2_flag(u_profile, ast_true(value), USER_OPT_STARTMUTED);
156 } else if (!strcasecmp(name, "music_on_hold_when_empty")) {
157 ast_set2_flag(u_profile, ast_true(value), USER_OPT_MUSICONHOLD);
158 } else if (!strcasecmp(name, "quiet")) {
159 ast_set2_flag(u_profile, ast_true(value), USER_OPT_QUIET);
160 } else if (!strcasecmp(name, "announce_user_count_all")) {
161 if (ast_true(value)) {
162 u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL;
163 } else if (ast_false(value)) {
164 u_profile->flags = u_profile->flags & ~USER_OPT_ANNOUNCEUSERCOUNTALL;
165 } else if (sscanf(value, "%30u", &u_profile->announce_user_count_all_after) == 1) {
166 u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL;
170 } else if (!strcasecmp(name, "announce_user_count")) {
171 ast_set2_flag(u_profile, ast_true(value), USER_OPT_ANNOUNCEUSERCOUNT);
172 } else if (!strcasecmp(name, "announce_only_user")) {
173 u_profile->flags = ast_true(value) ?
174 u_profile->flags & ~USER_OPT_NOONLYPERSON :
175 u_profile->flags | USER_OPT_NOONLYPERSON;
176 } else if (!strcasecmp(name, "wait_marked")) {
177 ast_set2_flag(u_profile, ast_true(value), USER_OPT_WAITMARKED);
178 } else if (!strcasecmp(name, "end_marked")) {
179 ast_set2_flag(u_profile, ast_true(value), USER_OPT_ENDMARKED);
180 } else if (!strcasecmp(name, "talk_detection_events")) {
181 ast_set2_flag(u_profile, ast_true(value), USER_OPT_TALKER_DETECT);
182 } else if (!strcasecmp(name, "dtmf_passthrough")) {
183 ast_set2_flag(u_profile, ast_true(value), USER_OPT_DTMF_PASS);
184 } else if (!strcasecmp(name, "announce_join_leave")) {
185 ast_set2_flag(u_profile, ast_true(value), USER_OPT_ANNOUNCE_JOIN_LEAVE);
186 } else if (!strcasecmp(name, "pin")) {
187 ast_copy_string(u_profile->pin, value, sizeof(u_profile->pin));
188 } else if (!strcasecmp(name, "music_on_hold_class")) {
189 ast_copy_string(u_profile->moh_class, value, sizeof(u_profile->moh_class));
190 } else if (!strcasecmp(name, "denoise")) {
191 ast_set2_flag(u_profile, ast_true(value), USER_OPT_DENOISE);
192 } else if (!strcasecmp(name, "dsp_talking_threshold")) {
193 if (sscanf(value, "%30u", &u_profile->talking_threshold) != 1) {
196 } else if (!strcasecmp(name, "dsp_silence_threshold")) {
197 if (sscanf(value, "%30u", &u_profile->silence_threshold) != 1) {
200 } else if (!strcasecmp(name, "dsp_drop_silence")) {
201 ast_set2_flag(u_profile, ast_true(value), USER_OPT_DROP_SILENCE);
202 } else if (!strcasecmp(name, "template")) {
203 if (!(conf_find_user_profile(NULL, value, u_profile))) {
206 } else if (!strcasecmp(name, "jitterbuffer")) {
207 ast_set2_flag(u_profile, ast_true(value), USER_OPT_JITTERBUFFER);
214 static int set_sound(const char *sound_name, const char *sound_file, struct bridge_profile_sounds *sounds)
216 if (ast_strlen_zero(sound_file)) {
220 if (!strcasecmp(sound_name, "sound_only_person")) {
221 ast_string_field_set(sounds, onlyperson, sound_file);
222 } else if (!strcasecmp(sound_name, "sound_has_joined")) {
223 ast_string_field_set(sounds, hasjoin, sound_file);
224 } else if (!strcasecmp(sound_name, "sound_has_left")) {
225 ast_string_field_set(sounds, hasleft, sound_file);
226 } else if (!strcasecmp(sound_name, "sound_kicked")) {
227 ast_string_field_set(sounds, kicked, sound_file);
228 } else if (!strcasecmp(sound_name, "sound_muted")) {
229 ast_string_field_set(sounds, muted, sound_file);
230 } else if (!strcasecmp(sound_name, "sound_unmuted")) {
231 ast_string_field_set(sounds, unmuted, sound_file);
232 } else if (!strcasecmp(sound_name, "sound_there_are")) {
233 ast_string_field_set(sounds, thereare, sound_file);
234 } else if (!strcasecmp(sound_name, "sound_other_in_party")) {
235 ast_string_field_set(sounds, otherinparty, sound_file);
236 } else if (!strcasecmp(sound_name, "sound_place_into_conference")) {
237 ast_string_field_set(sounds, placeintoconf, sound_file);
238 } else if (!strcasecmp(sound_name, "sound_wait_for_leader")) {
239 ast_string_field_set(sounds, waitforleader, sound_file);
240 } else if (!strcasecmp(sound_name, "sound_get_pin")) {
241 ast_string_field_set(sounds, getpin, sound_file);
242 } else if (!strcasecmp(sound_name, "sound_invalid_pin")) {
243 ast_string_field_set(sounds, invalidpin, sound_file);
244 } else if (!strcasecmp(sound_name, "sound_locked")) {
245 ast_string_field_set(sounds, locked, sound_file);
246 } else if (!strcasecmp(sound_name, "sound_unlocked_now")) {
247 ast_string_field_set(sounds, unlockednow, sound_file);
248 } else if (!strcasecmp(sound_name, "sound_locked_now")) {
249 ast_string_field_set(sounds, lockednow, sound_file);
250 } else if (!strcasecmp(sound_name, "sound_error_menu")) {
251 ast_string_field_set(sounds, errormenu, sound_file);
252 } else if (!strcasecmp(sound_name, "sound_join")) {
253 ast_string_field_set(sounds, join, sound_file);
254 } else if (!strcasecmp(sound_name, "sound_leave")) {
255 ast_string_field_set(sounds, leave, sound_file);
262 static int set_bridge_option(const char *name, const char *value, struct bridge_profile *b_profile)
264 if (!strcasecmp(name, "internal_sample_rate")) {
265 if (!strcasecmp(value, "auto")) {
266 b_profile->internal_sample_rate = 0;
267 } else if (sscanf(value, "%30u", &b_profile->internal_sample_rate) != 1) {
270 } else if (!strcasecmp(name, "mixing_interval")) {
271 if (sscanf(value, "%30u", &b_profile->mix_interval) != 1) {
274 switch (b_profile->mix_interval) {
281 ast_log(LOG_WARNING, "invalid mixing interval %u\n", b_profile->mix_interval);
282 b_profile->mix_interval = 0;
285 } else if (!strcasecmp(name, "record_conference")) {
286 ast_set2_flag(b_profile, ast_true(value), BRIDGE_OPT_RECORD_CONFERENCE);
287 } else if (!strcasecmp(name, "video_mode")) {
288 if (!strcasecmp(value, "first_marked")) {
289 ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED);
290 } else if (!strcasecmp(value, "last_marked")) {
291 ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED);
292 } else if (!strcasecmp(value, "follow_talker")) {
293 ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
295 } else if (!strcasecmp(name, "max_members")) {
296 if (sscanf(value, "%30u", &b_profile->max_members) != 1) {
299 } else if (!strcasecmp(name, "record_file")) {
300 ast_copy_string(b_profile->rec_file, value, sizeof(b_profile->rec_file));
301 } else if (strlen(name) >= 5 && !strncasecmp(name, "sound", 5)) {
302 if (set_sound(name, value, b_profile->sounds)) {
305 } else if (!strcasecmp(name, "template")) { /* Only documented for use in CONFBRIDGE dialplan function */
306 struct bridge_profile *tmp = b_profile;
307 struct bridge_profile_sounds *sounds = bridge_profile_sounds_alloc();
308 struct bridge_profile_sounds *oldsounds = b_profile->sounds;
312 if (!(conf_find_bridge_profile(NULL, value, tmp))) {
316 /* Using a bridge profile as a template is a little complicated due to the sounds. Since the sounds
317 * structure of a dynamic profile will need to be altered, a completely new sounds structure must be
318 * create instead of simply holding a reference to the one built by the config file. */
319 ast_string_field_set(sounds, onlyperson, tmp->sounds->onlyperson);
320 ast_string_field_set(sounds, hasjoin, tmp->sounds->hasjoin);
321 ast_string_field_set(sounds, hasleft, tmp->sounds->hasleft);
322 ast_string_field_set(sounds, kicked, tmp->sounds->kicked);
323 ast_string_field_set(sounds, muted, tmp->sounds->muted);
324 ast_string_field_set(sounds, unmuted, tmp->sounds->unmuted);
325 ast_string_field_set(sounds, thereare, tmp->sounds->thereare);
326 ast_string_field_set(sounds, otherinparty, tmp->sounds->otherinparty);
327 ast_string_field_set(sounds, placeintoconf, tmp->sounds->placeintoconf);
328 ast_string_field_set(sounds, waitforleader, tmp->sounds->waitforleader);
329 ast_string_field_set(sounds, getpin, tmp->sounds->getpin);
330 ast_string_field_set(sounds, invalidpin, tmp->sounds->invalidpin);
331 ast_string_field_set(sounds, locked, tmp->sounds->locked);
332 ast_string_field_set(sounds, unlockednow, tmp->sounds->unlockednow);
333 ast_string_field_set(sounds, lockednow, tmp->sounds->lockednow);
334 ast_string_field_set(sounds, errormenu, tmp->sounds->errormenu);
336 ao2_ref(tmp->sounds, -1); /* sounds struct copied over to it from the template by reference only. */
337 ao2_ref(oldsounds,-1); /* original sounds struct we don't need anymore */
338 tmp->sounds = sounds; /* the new sounds struct that is a deep copy of the one from the template. */
346 /*! CONFBRIDGE dialplan function functions and channel datastore. */
347 struct func_confbridge_data {
348 struct bridge_profile b_profile;
349 struct user_profile u_profile;
350 unsigned int b_usable:1; /*!< Tells if bridge profile is usable or not */
351 unsigned int u_usable:1; /*!< Tells if user profile is usable or not */
353 static void func_confbridge_destroy_cb(void *data)
355 struct func_confbridge_data *b_data = data;
356 conf_bridge_profile_destroy(&b_data->b_profile);
359 static const struct ast_datastore_info confbridge_datastore = {
360 .type = "confbridge",
361 .destroy = func_confbridge_destroy_cb
363 int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value)
365 struct ast_datastore *datastore = NULL;
366 struct func_confbridge_data *b_data = NULL;
369 AST_DECLARE_APP_ARGS(args,
374 /* parse all the required arguments and make sure they exist. */
375 if (ast_strlen_zero(data) || ast_strlen_zero(value)) {
378 parse = ast_strdupa(data);
379 AST_STANDARD_APP_ARGS(args, parse);
380 if (ast_strlen_zero(args.type) || ast_strlen_zero(args.option)) {
384 ast_channel_lock(chan);
385 if (!(datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL))) {
386 ast_channel_unlock(chan);
388 if (!(datastore = ast_datastore_alloc(&confbridge_datastore, NULL))) {
391 if (!(b_data = ast_calloc(1, sizeof(*b_data)))) {
392 ast_datastore_free(datastore);
395 if (!(b_data->b_profile.sounds = bridge_profile_sounds_alloc())) {
396 ast_datastore_free(datastore);
400 datastore->data = b_data;
403 ast_channel_unlock(chan);
404 b_data = datastore->data;
407 /* SET(CONFBRIDGE(type,option)=value) */
408 if (!strcasecmp(args.type, "bridge") && !set_bridge_option(args.option, value, &b_data->b_profile)) {
409 b_data->b_usable = 1;
410 } else if (!strcasecmp(args.type, "user") && !set_user_option(args.option, value, &b_data->u_profile)) {
411 b_data->u_usable = 1;
413 ast_log(LOG_WARNING, "Profile type \"%s\" can not be set in CONFBRIDGE function with option \"%s\" and value \"%s\"\n",
414 args.type, args.option, value);
418 ast_channel_lock(chan);
419 ast_channel_datastore_add(chan, datastore);
420 ast_channel_unlock(chan);
425 ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
427 ast_datastore_free(datastore);
433 * \brief Parse the bridge profile options
435 static int parse_bridge(const char *cat, struct ast_config *cfg)
437 struct ast_variable *var;
438 struct bridge_profile tmp;
439 struct bridge_profile *b_profile;
441 ast_copy_string(tmp.name, cat, sizeof(tmp.name));
442 if ((b_profile = ao2_find(bridge_profiles, &tmp, OBJ_POINTER))) {
443 b_profile->delme = 0;
444 } else if ((b_profile = ao2_alloc(sizeof(*b_profile), NULL))) {
445 ast_copy_string(b_profile->name, cat, sizeof(b_profile->name));
446 ao2_link(bridge_profiles, b_profile);
453 b_profile->internal_sample_rate = 0;
454 b_profile->flags = 0;
455 b_profile->max_members = 0;
456 b_profile->mix_interval = 0;
457 memset(b_profile->rec_file, 0, sizeof(b_profile->rec_file));
458 if (b_profile->sounds) {
459 ao2_ref(b_profile->sounds, -1); /* sounds is read only. Once it has been created
460 * it can never be altered. This prevents having to
461 * do any locking after it is built from the config. */
462 b_profile->sounds = NULL;
465 if (!(b_profile->sounds = bridge_profile_sounds_alloc())) {
466 ao2_unlock(b_profile);
467 ao2_ref(b_profile, -1);
468 ao2_unlink(bridge_profiles, b_profile);
472 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
473 if (!strcasecmp(var->name, "type")) {
475 } else if (set_bridge_option(var->name, var->value, b_profile)) {
476 ast_log(LOG_WARNING, "Invalid: '%s' at line %d of %s is not supported.\n",
477 var->name, var->lineno, CONFBRIDGE_CONFIG);
480 ao2_unlock(b_profile);
482 ao2_ref(b_profile, -1);
486 static int parse_user(const char *cat, struct ast_config *cfg)
488 struct ast_variable *var;
489 struct user_profile tmp;
490 struct user_profile *u_profile;
492 ast_copy_string(tmp.name, cat, sizeof(tmp.name));
493 if ((u_profile = ao2_find(user_profiles, &tmp, OBJ_POINTER))) {
494 u_profile->delme = 0;
495 } else if ((u_profile = ao2_alloc(sizeof(*u_profile), NULL))) {
496 ast_copy_string(u_profile->name, cat, sizeof(u_profile->name));
497 ao2_link(user_profiles, u_profile);
504 u_profile->flags = 0;
505 u_profile->announce_user_count_all_after = 0;
506 u_profile->silence_threshold = DEFAULT_SILENCE_THRESHOLD;
507 u_profile->talking_threshold = DEFAULT_TALKING_THRESHOLD;
508 memset(u_profile->pin, 0, sizeof(u_profile->pin));
509 memset(u_profile->moh_class, 0, sizeof(u_profile->moh_class));
510 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
511 if (!strcasecmp(var->name, "type")) {
513 } else if (set_user_option(var->name, var->value, u_profile)) {
514 ast_log(LOG_WARNING, "Invalid option '%s' at line %d of %s is not supported.\n",
515 var->name, var->lineno, CONFBRIDGE_CONFIG);
518 ao2_unlock(u_profile);
520 ao2_ref(u_profile, -1);
524 static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum conf_menu_action_id id, char *databuf)
526 struct conf_menu_action *menu_action = ast_calloc(1, sizeof(*menu_action));
531 menu_action->id = id;
534 case MENU_ACTION_NOOP:
535 case MENU_ACTION_TOGGLE_MUTE:
536 case MENU_ACTION_INCREASE_LISTENING:
537 case MENU_ACTION_DECREASE_LISTENING:
538 case MENU_ACTION_INCREASE_TALKING:
539 case MENU_ACTION_DECREASE_TALKING:
540 case MENU_ACTION_RESET_LISTENING:
541 case MENU_ACTION_RESET_TALKING:
542 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
543 case MENU_ACTION_ADMIN_KICK_LAST:
544 case MENU_ACTION_LEAVE:
545 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
546 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
548 case MENU_ACTION_PLAYBACK:
549 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
550 if (!(ast_strlen_zero(databuf))) {
551 ast_copy_string(menu_action->data.playback_file, databuf, sizeof(menu_action->data.playback_file));
553 ast_free(menu_action);
557 case MENU_ACTION_DIALPLAN_EXEC:
558 if (!(ast_strlen_zero(databuf))) {
559 AST_DECLARE_APP_ARGS(args,
560 AST_APP_ARG(context);
562 AST_APP_ARG(priority);
564 AST_STANDARD_APP_ARGS(args, databuf);
565 if (!ast_strlen_zero(args.context)) {
566 ast_copy_string(menu_action->data.dialplan_args.context,
568 sizeof(menu_action->data.dialplan_args.context));
570 if (!ast_strlen_zero(args.exten)) {
571 ast_copy_string(menu_action->data.dialplan_args.exten,
573 sizeof(menu_action->data.dialplan_args.exten));
575 menu_action->data.dialplan_args.priority = 1; /* 1 by default */
576 if (!ast_strlen_zero(args.priority) &&
577 (sscanf(args.priority, "%30u", &menu_action->data.dialplan_args.priority) != 1)) {
578 /* invalid priority */
579 ast_free(menu_action);
583 ast_free(menu_action);
588 AST_LIST_INSERT_TAIL(&menu_entry->actions, menu_action, action);
593 static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *action_names)
595 struct conf_menu_entry *menu_entry = NULL, *cur = NULL;
597 char *tmp_action_names = ast_strdupa(action_names);
602 char *delimiter = ",";
604 if (!(menu_entry = ast_calloc(1, sizeof(*menu_entry)))) {
612 unsigned int action_len;
614 if (ast_strlen_zero(tmp_action_names)) {
617 startbrace = strchr(tmp_action_names, '(');
618 endbrace = strchr(tmp_action_names, ')');
619 comma = strchr(tmp_action_names, ',');
621 /* If the next action has brackets with comma delimited arguments in it,
622 * make the delimeter ')' instead of a comma to preserve the argments */
623 if (startbrace && endbrace && comma && (comma > startbrace && comma < endbrace)) {
629 if (!(action = strsep(&tmp_action_names, delimiter))) {
633 action = ast_strip(action);
634 if (ast_strlen_zero(action)) {
638 action_len = strlen(action);
639 ast_copy_string(menu_entry->dtmf, dtmf, sizeof(menu_entry->dtmf));
640 if (!strcasecmp(action, "toggle_mute")) {
641 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_TOGGLE_MUTE, NULL);
642 } else if (!strcasecmp(action, "no_op")) {
643 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_NOOP, NULL);
644 } else if (!strcasecmp(action, "increase_listening_volume")) {
645 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_LISTENING, NULL);
646 } else if (!strcasecmp(action, "decrease_listening_volume")) {
647 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_LISTENING, NULL);
648 } else if (!strcasecmp(action, "increase_talking_volume")) {
649 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_TALKING, NULL);
650 } else if (!strcasecmp(action, "reset_listening_volume")) {
651 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_LISTENING, NULL);
652 } else if (!strcasecmp(action, "reset_talking_volume")) {
653 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_TALKING, NULL);
654 } else if (!strcasecmp(action, "decrease_talking_volume")) {
655 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_TALKING, NULL);
656 } else if (!strcasecmp(action, "admin_toggle_conference_lock")) {
657 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_LOCK, NULL);
658 } else if (!strcasecmp(action, "admin_kick_last")) {
659 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_KICK_LAST, NULL);
660 } else if (!strcasecmp(action, "leave_conference")) {
661 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_LEAVE, NULL);
662 } else if (!strcasecmp(action, "set_as_single_video_src")) {
663 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_SET_SINGLE_VIDEO_SRC, NULL);
664 } else if (!strcasecmp(action, "release_as_single_video_src")) {
665 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC, NULL);
666 } else if (!strncasecmp(action, "dialplan_exec(", 14)) {
667 ast_copy_string(buf, action, sizeof(buf));
669 if ((action_args = strchr(action, '('))) {
672 /* it is possible that this argument may or may not
673 * have a closing brace at this point, it all depends on if
674 * comma delimited arguments were provided */
675 if ((tmp = strchr(action, ')'))) {
678 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DIALPLAN_EXEC, action_args);
679 } else if (action_len >= 21 && !strncasecmp(action, "playback_and_continue(", 22)) {
680 ast_copy_string(buf, action, sizeof(buf));
682 if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
686 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK_AND_CONTINUE, action_args);
687 } else if (action_len >= 8 && !strncasecmp(action, "playback(", 9)) {
688 ast_copy_string(buf, action, sizeof(buf));
690 if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
694 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK, action_args);
698 /* if adding any of the actions failed, bail */
700 struct conf_menu_action *action;
701 while ((action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
704 ast_free(menu_entry);
708 /* remove any list entry with an identical DTMF sequence for overrides */
709 AST_LIST_TRAVERSE_SAFE_BEGIN(&menu->entries, cur, entry) {
710 if (!strcasecmp(cur->dtmf, menu_entry->dtmf)) {
711 AST_LIST_REMOVE_CURRENT(entry);
716 AST_LIST_TRAVERSE_SAFE_END;
718 AST_LIST_INSERT_TAIL(&menu->entries, menu_entry, entry);
722 static int parse_menu(const char *cat, struct ast_config *cfg)
724 struct ast_variable *var;
725 struct conf_menu tmp;
726 struct conf_menu *menu;
728 ast_copy_string(tmp.name, cat, sizeof(tmp.name));
729 if ((menu = ao2_find(menus, &tmp, OBJ_POINTER))) {
731 } else if ((menu = ao2_alloc(sizeof(*menu), menu_destructor))) {
732 ast_copy_string(menu->name, cat, sizeof(menu->name));
733 ao2_link(menus, menu);
739 /* this isn't freeing the menu, just destroying the menu list so it can be rebuilt.*/
740 menu_destructor(menu);
741 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
742 if (!strcasecmp(var->name, "type")) {
744 } else if (add_menu_entry(menu, var->name, var->value)) {
745 ast_log(LOG_WARNING, "Unknown option '%s' at line %d of %s is not supported.\n",
746 var->name, var->lineno, CONFBRIDGE_CONFIG);
755 static char *complete_user_profile_name(const char *line, const char *word, int pos, int state)
759 int wordlen = strlen(word);
760 struct ao2_iterator i;
761 struct user_profile *u_profile = NULL;
763 i = ao2_iterator_init(user_profiles, 0);
764 while ((u_profile = ao2_iterator_next(&i))) {
765 if (!strncasecmp(u_profile->name, word, wordlen) && ++which > state) {
766 res = ast_strdup(u_profile->name);
767 ao2_ref(u_profile, -1);
770 ao2_ref(u_profile, -1);
772 ao2_iterator_destroy(&i);
777 static char *handle_cli_confbridge_show_user_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
779 struct ao2_iterator it;
780 struct user_profile *u_profile;
784 e->command = "confbridge show profile users";
786 "Usage confbridge show profile users\n";
792 ast_cli(a->fd,"--------- User Profiles -----------\n");
793 ao2_lock(user_profiles);
794 it = ao2_iterator_init(user_profiles, 0);
795 while ((u_profile = ao2_iterator_next(&it))) {
796 ast_cli(a->fd,"%s\n", u_profile->name);
797 ao2_ref(u_profile, -1);
799 ao2_iterator_destroy(&it);
800 ao2_unlock(user_profiles);
804 static char *handle_cli_confbridge_show_user_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
806 struct user_profile u_profile;
810 e->command = "confbridge show profile user";
812 "Usage confbridge show profile user [<profile name>]\n";
816 return complete_user_profile_name(a->line, a->word, a->pos, a->n);
822 return CLI_SHOWUSAGE;
825 if (!(conf_find_user_profile(NULL, a->argv[4], &u_profile))) {
826 ast_cli(a->fd, "No conference user profile named '%s' found!\n", a->argv[4]);
830 ast_cli(a->fd,"--------------------------------------------\n");
831 ast_cli(a->fd,"Name: %s\n",
833 ast_cli(a->fd,"Admin: %s\n",
834 u_profile.flags & USER_OPT_ADMIN ?
836 ast_cli(a->fd,"Marked User: %s\n",
837 u_profile.flags & USER_OPT_MARKEDUSER ?
839 ast_cli(a->fd,"Start Muted: %s\n",
840 u_profile.flags & USER_OPT_STARTMUTED?
842 ast_cli(a->fd,"MOH When Empty: %s\n",
843 u_profile.flags & USER_OPT_MUSICONHOLD ?
844 "enabled" : "disabled");
845 ast_cli(a->fd,"MOH Class: %s\n",
846 ast_strlen_zero(u_profile.moh_class) ?
847 "default" : u_profile.moh_class);
848 ast_cli(a->fd,"Quiet: %s\n",
849 u_profile.flags & USER_OPT_QUIET ?
850 "enabled" : "disabled");
851 ast_cli(a->fd,"Wait Marked: %s\n",
852 u_profile.flags & USER_OPT_WAITMARKED ?
853 "enabled" : "disabled");
854 ast_cli(a->fd,"END Marked: %s\n",
855 u_profile.flags & USER_OPT_ENDMARKED ?
856 "enabled" : "disabled");
857 ast_cli(a->fd,"Drop_silence: %s\n",
858 u_profile.flags & USER_OPT_DROP_SILENCE ?
859 "enabled" : "disabled");
860 ast_cli(a->fd,"Silence Threshold: %dms\n",
861 u_profile.silence_threshold);
862 ast_cli(a->fd,"Talking Threshold: %dms\n",
863 u_profile.talking_threshold);
864 ast_cli(a->fd,"Denoise: %s\n",
865 u_profile.flags & USER_OPT_DENOISE ?
866 "enabled" : "disabled");
867 ast_cli(a->fd,"Jitterbuffer: %s\n",
868 u_profile.flags & USER_OPT_JITTERBUFFER ?
869 "enabled" : "disabled");
870 ast_cli(a->fd,"Talk Detect Events: %s\n",
871 u_profile.flags & USER_OPT_TALKER_DETECT ?
872 "enabled" : "disabled");
873 ast_cli(a->fd,"DTMF Pass Through: %s\n",
874 u_profile.flags & USER_OPT_DTMF_PASS ?
875 "enabled" : "disabled");
876 ast_cli(a->fd,"PIN: %s\n",
877 ast_strlen_zero(u_profile.pin) ?
878 "None" : u_profile.pin);
879 ast_cli(a->fd,"Announce User Count: %s\n",
880 u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNT ?
881 "enabled" : "disabled");
882 ast_cli(a->fd,"Announce join/leave: %s\n",
883 u_profile.flags & USER_OPT_ANNOUNCE_JOIN_LEAVE ?
884 "enabled" : "disabled");
885 ast_cli(a->fd,"Announce User Count all: %s\n",
886 u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNTALL ?
887 "enabled" : "disabled");
893 static char *complete_bridge_profile_name(const char *line, const char *word, int pos, int state)
897 int wordlen = strlen(word);
898 struct ao2_iterator i;
899 struct bridge_profile *b_profile = NULL;
901 i = ao2_iterator_init(bridge_profiles, 0);
902 while ((b_profile = ao2_iterator_next(&i))) {
903 if (!strncasecmp(b_profile->name, word, wordlen) && ++which > state) {
904 res = ast_strdup(b_profile->name);
905 ao2_ref(b_profile, -1);
908 ao2_ref(b_profile, -1);
910 ao2_iterator_destroy(&i);
915 static char *handle_cli_confbridge_show_bridge_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
917 struct ao2_iterator it;
918 struct bridge_profile *b_profile;
922 e->command = "confbridge show profile bridges";
924 "Usage confbridge show profile bridges\n";
930 ast_cli(a->fd,"--------- Bridge Profiles -----------\n");
931 ao2_lock(bridge_profiles);
932 it = ao2_iterator_init(bridge_profiles, 0);
933 while ((b_profile = ao2_iterator_next(&it))) {
934 ast_cli(a->fd,"%s\n", b_profile->name);
935 ao2_ref(b_profile, -1);
937 ao2_iterator_destroy(&it);
938 ao2_unlock(bridge_profiles);
943 static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
945 struct bridge_profile b_profile;
950 e->command = "confbridge show profile bridge";
952 "Usage confbridge show profile bridge <profile name>\n";
956 return complete_bridge_profile_name(a->line, a->word, a->pos, a->n);
962 return CLI_SHOWUSAGE;
965 if (!(conf_find_bridge_profile(NULL, a->argv[4], &b_profile))) {
966 ast_cli(a->fd, "No conference bridge profile named '%s' found!\n", a->argv[4]);
970 ast_cli(a->fd,"--------------------------------------------\n");
971 ast_cli(a->fd,"Name: %s\n", b_profile.name);
973 if (b_profile.internal_sample_rate) {
974 snprintf(tmp, sizeof(tmp), "%d", b_profile.internal_sample_rate);
976 ast_copy_string(tmp, "auto", sizeof(tmp));
978 ast_cli(a->fd,"Internal Sample Rate: %s\n", tmp);
980 if (b_profile.mix_interval) {
981 ast_cli(a->fd,"Mixing Interval: %d\n", b_profile.mix_interval);
983 ast_cli(a->fd,"Mixing Interval: Default 20ms\n");
986 ast_cli(a->fd,"Record Conference: %s\n",
987 b_profile.flags & BRIDGE_OPT_RECORD_CONFERENCE ?
990 ast_cli(a->fd,"Record File: %s\n",
991 ast_strlen_zero(b_profile.rec_file) ? "Auto Generated" :
994 if (b_profile.max_members) {
995 ast_cli(a->fd,"Max Members: %d\n", b_profile.max_members);
997 ast_cli(a->fd,"Max Members: No Limit\n");
1000 if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_LAST_MARKED) {
1001 ast_cli(a->fd, "Video Mode: last_marked\n");
1002 } else if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) {
1003 ast_cli(a->fd, "Video Mode: first_marked\n");
1004 } else if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER) {
1005 ast_cli(a->fd, "Video Mode: follow_talker\n");
1007 ast_cli(a->fd, "Video Mode: no video\n");
1010 ast_cli(a->fd,"sound_join: %s\n", conf_get_sound(CONF_SOUND_JOIN, b_profile.sounds));
1011 ast_cli(a->fd,"sound_leave: %s\n", conf_get_sound(CONF_SOUND_LEAVE, b_profile.sounds));
1012 ast_cli(a->fd,"sound_only_person: %s\n", conf_get_sound(CONF_SOUND_ONLY_PERSON, b_profile.sounds));
1013 ast_cli(a->fd,"sound_has_joined: %s\n", conf_get_sound(CONF_SOUND_HAS_JOINED, b_profile.sounds));
1014 ast_cli(a->fd,"sound_has_left: %s\n", conf_get_sound(CONF_SOUND_HAS_LEFT, b_profile.sounds));
1015 ast_cli(a->fd,"sound_kicked: %s\n", conf_get_sound(CONF_SOUND_KICKED, b_profile.sounds));
1016 ast_cli(a->fd,"sound_muted: %s\n", conf_get_sound(CONF_SOUND_MUTED, b_profile.sounds));
1017 ast_cli(a->fd,"sound_unmuted: %s\n", conf_get_sound(CONF_SOUND_UNMUTED, b_profile.sounds));
1018 ast_cli(a->fd,"sound_there_are: %s\n", conf_get_sound(CONF_SOUND_THERE_ARE, b_profile.sounds));
1019 ast_cli(a->fd,"sound_other_in_party: %s\n", conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, b_profile.sounds));
1020 ast_cli(a->fd,"sound_place_into_conference: %s\n", conf_get_sound(CONF_SOUND_PLACE_IN_CONF, b_profile.sounds));
1021 ast_cli(a->fd,"sound_wait_for_leader: %s\n", conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, b_profile.sounds));
1022 ast_cli(a->fd,"sound_get_pin: %s\n", conf_get_sound(CONF_SOUND_GET_PIN, b_profile.sounds));
1023 ast_cli(a->fd,"sound_invalid_pin: %s\n", conf_get_sound(CONF_SOUND_INVALID_PIN, b_profile.sounds));
1024 ast_cli(a->fd,"sound_locked: %s\n", conf_get_sound(CONF_SOUND_LOCKED, b_profile.sounds));
1025 ast_cli(a->fd,"sound_unlocked_now: %s\n", conf_get_sound(CONF_SOUND_UNLOCKED_NOW, b_profile.sounds));
1026 ast_cli(a->fd,"sound_lockednow: %s\n", conf_get_sound(CONF_SOUND_LOCKED_NOW, b_profile.sounds));
1027 ast_cli(a->fd,"sound_error_menu: %s\n", conf_get_sound(CONF_SOUND_ERROR_MENU, b_profile.sounds));
1028 ast_cli(a->fd,"\n");
1030 conf_bridge_profile_destroy(&b_profile);
1034 static char *complete_menu_name(const char *line, const char *word, int pos, int state)
1038 int wordlen = strlen(word);
1039 struct ao2_iterator i;
1040 struct conf_menu *menu = NULL;
1042 i = ao2_iterator_init(menus, 0);
1043 while ((menu = ao2_iterator_next(&i))) {
1044 if (!strncasecmp(menu->name, word, wordlen) && ++which > state) {
1045 res = ast_strdup(menu->name);
1051 ao2_iterator_destroy(&i);
1056 static char *handle_cli_confbridge_show_menus(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1058 struct ao2_iterator it;
1059 struct conf_menu *menu;
1063 e->command = "confbridge show menus";
1065 "Usage confbridge show profile menus\n";
1071 ast_cli(a->fd,"--------- Menus -----------\n");
1073 it = ao2_iterator_init(menus, 0);
1074 while ((menu = ao2_iterator_next(&it))) {
1075 ast_cli(a->fd,"%s\n", menu->name);
1078 ao2_iterator_destroy(&it);
1084 static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1086 struct conf_menu tmp;
1087 struct conf_menu *menu;
1088 struct conf_menu_entry *menu_entry = NULL;
1089 struct conf_menu_action *menu_action = NULL;
1093 e->command = "confbridge show menu";
1095 "Usage confbridge show menu [<menu name>]\n";
1099 return complete_menu_name(a->line, a->word, a->pos, a->n);
1105 return CLI_SHOWUSAGE;
1108 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
1109 if (!(menu = ao2_find(menus, &tmp, OBJ_POINTER))) {
1110 ast_cli(a->fd, "No conference menu named '%s' found!\n", a->argv[3]);
1115 ast_cli(a->fd,"Name: %s\n", menu->name);
1116 AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
1118 ast_cli(a->fd, "%s=", menu_entry->dtmf);
1119 AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
1121 ast_cli(a->fd, ", ");
1123 switch (menu_action->id) {
1124 case MENU_ACTION_TOGGLE_MUTE:
1125 ast_cli(a->fd, "toggle_mute");
1127 case MENU_ACTION_NOOP:
1128 ast_cli(a->fd, "no_op");
1130 case MENU_ACTION_INCREASE_LISTENING:
1131 ast_cli(a->fd, "increase_listening_volume");
1133 case MENU_ACTION_DECREASE_LISTENING:
1134 ast_cli(a->fd, "decrease_listening_volume");
1136 case MENU_ACTION_RESET_LISTENING:
1137 ast_cli(a->fd, "reset_listening_volume");
1139 case MENU_ACTION_RESET_TALKING:
1140 ast_cli(a->fd, "reset_talking_volume");
1142 case MENU_ACTION_INCREASE_TALKING:
1143 ast_cli(a->fd, "increase_talking_volume");
1145 case MENU_ACTION_DECREASE_TALKING:
1146 ast_cli(a->fd, "decrease_talking_volume");
1148 case MENU_ACTION_PLAYBACK:
1149 ast_cli(a->fd, "playback(%s)", menu_action->data.playback_file);
1151 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
1152 ast_cli(a->fd, "playback_and_continue(%s)", menu_action->data.playback_file);
1154 case MENU_ACTION_DIALPLAN_EXEC:
1155 ast_cli(a->fd, "dialplan_exec(%s,%s,%d)",
1156 menu_action->data.dialplan_args.context,
1157 menu_action->data.dialplan_args.exten,
1158 menu_action->data.dialplan_args.priority);
1160 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
1161 ast_cli(a->fd, "admin_toggle_conference_lock");
1163 case MENU_ACTION_ADMIN_KICK_LAST:
1164 ast_cli(a->fd, "admin_kick_last");
1166 case MENU_ACTION_LEAVE:
1167 ast_cli(a->fd, "leave_conference");
1169 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
1170 ast_cli(a->fd, "set_as_single_video_src");
1172 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
1173 ast_cli(a->fd, "release_as_single_video_src");
1178 ast_cli(a->fd,"\n");
1187 static struct ast_cli_entry cli_confbridge_parser[] = {
1188 AST_CLI_DEFINE(handle_cli_confbridge_show_user_profile, "Show a conference user profile."),
1189 AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profile, "Show a conference bridge profile."),
1190 AST_CLI_DEFINE(handle_cli_confbridge_show_menu, "Show a conference menu"),
1191 AST_CLI_DEFINE(handle_cli_confbridge_show_user_profiles, "Show a list of conference user profiles."),
1192 AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profiles, "Show a list of conference bridge profiles."),
1193 AST_CLI_DEFINE(handle_cli_confbridge_show_menus, "Show a list of conference menus"),
1197 static int conf_parse_init(void)
1199 if (!(user_profiles = ao2_container_alloc(283, user_hash_cb, user_cmp_cb))) {
1200 conf_destroy_config();
1204 if (!(bridge_profiles = ao2_container_alloc(283, bridge_hash_cb, bridge_cmp_cb))) {
1205 conf_destroy_config();
1209 if (!(menus = ao2_container_alloc(283, menu_hash_cb, menu_cmp_cb))) {
1210 conf_destroy_config();
1214 ast_cli_register_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser));
1219 void conf_destroy_config()
1221 if (user_profiles) {
1222 ao2_ref(user_profiles, -1);
1223 user_profiles = NULL;
1225 if (bridge_profiles) {
1226 ao2_ref(bridge_profiles, -1);
1227 bridge_profiles = NULL;
1234 ast_cli_unregister_multiple(cli_confbridge_parser, sizeof(cli_confbridge_parser) / sizeof(struct ast_cli_entry));
1237 static void remove_all_delme(void)
1239 ao2_callback(user_profiles, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, match_user_delme_cb, NULL);
1240 ao2_callback(bridge_profiles, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, match_bridge_delme_cb, NULL);
1241 ao2_callback(menus, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, match_menu_delme_cb, NULL);
1244 static void mark_all_delme(void)
1246 ao2_callback(user_profiles, OBJ_NODATA | OBJ_MULTIPLE, user_mark_delme_cb, NULL);
1247 ao2_callback(bridge_profiles, OBJ_NODATA | OBJ_MULTIPLE, bridge_mark_delme_cb, NULL);
1248 ao2_callback(menus, OBJ_NODATA | OBJ_MULTIPLE, menu_mark_delme_cb, NULL);
1251 int conf_load_config(int reload)
1253 struct ast_flags config_flags = { 0, };
1254 struct ast_config *cfg = ast_config_load(CONFBRIDGE_CONFIG, config_flags);
1255 const char *type = NULL;
1262 if (!cfg || cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
1268 while ((cat = ast_category_browse(cfg, cat))) {
1269 if (!(type = (ast_variable_retrieve(cfg, cat, "type")))) {
1270 if (strcasecmp(cat, "general")) {
1271 ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat);
1275 if (!strcasecmp(type, "bridge")) {
1276 parse_bridge(cat, cfg);
1277 } else if (!strcasecmp(type, "user")) {
1278 parse_user(cat, cfg);
1279 } else if (!strcasecmp(type, "menu")) {
1280 parse_menu(cat, cfg);
1291 static void conf_user_profile_copy(struct user_profile *dst, struct user_profile *src)
1293 memcpy(dst, src, sizeof(*dst));
1296 const struct user_profile *conf_find_user_profile(struct ast_channel *chan, const char *user_profile_name, struct user_profile *result)
1298 struct user_profile tmp;
1299 struct user_profile *tmp2;
1300 struct ast_datastore *datastore = NULL;
1301 struct func_confbridge_data *b_data = NULL;
1302 ast_copy_string(tmp.name, user_profile_name, sizeof(tmp.name));
1305 ast_channel_lock(chan);
1306 if ((datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL))) {
1307 ast_channel_unlock(chan);
1308 b_data = datastore->data;
1309 if (b_data->u_usable) {
1310 conf_user_profile_copy(result, &b_data->u_profile);
1314 ast_channel_unlock(chan);
1317 if (ast_strlen_zero(user_profile_name)) {
1318 user_profile_name = DEFAULT_USER_PROFILE;
1320 if (!(tmp2 = ao2_find(user_profiles, &tmp, OBJ_POINTER))) {
1324 conf_user_profile_copy(result, tmp2);
1331 void conf_bridge_profile_copy(struct bridge_profile *dst, struct bridge_profile *src)
1333 memcpy(dst, src, sizeof(*dst));
1335 ao2_ref(src->sounds, +1);
1339 void conf_bridge_profile_destroy(struct bridge_profile *b_profile)
1341 if (b_profile->sounds) {
1342 ao2_ref(b_profile->sounds, -1);
1343 b_profile->sounds = NULL;
1347 const struct bridge_profile *conf_find_bridge_profile(struct ast_channel *chan, const char *bridge_profile_name, struct bridge_profile *result)
1349 struct bridge_profile tmp;
1350 struct bridge_profile *tmp2;
1351 struct ast_datastore *datastore = NULL;
1352 struct func_confbridge_data *b_data = NULL;
1355 ast_channel_lock(chan);
1356 if ((datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL))) {
1357 ast_channel_unlock(chan);
1358 b_data = datastore->data;
1359 if (b_data->b_usable) {
1360 conf_bridge_profile_copy(result, &b_data->b_profile);
1364 ast_channel_unlock(chan);
1366 if (ast_strlen_zero(bridge_profile_name)) {
1367 bridge_profile_name = DEFAULT_BRIDGE_PROFILE;
1369 ast_copy_string(tmp.name, bridge_profile_name, sizeof(tmp.name));
1370 if (!(tmp2 = ao2_find(bridge_profiles, &tmp, OBJ_POINTER))) {
1374 conf_bridge_profile_copy(result, tmp2);
1381 struct dtmf_menu_hook_pvt {
1382 struct conference_bridge_user *conference_bridge_user;
1383 struct conf_menu_entry menu_entry;
1384 struct conf_menu *menu;
1387 static void menu_hook_destroy(void *hook_pvt)
1389 struct dtmf_menu_hook_pvt *pvt = hook_pvt;
1390 struct conf_menu_action *action = NULL;
1392 ao2_ref(pvt->menu, -1);
1394 while ((action = AST_LIST_REMOVE_HEAD(&pvt->menu_entry.actions, action))) {
1400 static int menu_hook_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1402 struct dtmf_menu_hook_pvt *pvt = hook_pvt;
1403 return conf_handle_dtmf(bridge_channel, pvt->conference_bridge_user, &pvt->menu_entry, pvt->menu);
1406 static int copy_menu_entry(struct conf_menu_entry *dst, struct conf_menu_entry *src)
1408 struct conf_menu_action *menu_action = NULL;
1409 struct conf_menu_action *new_menu_action = NULL;
1411 memcpy(dst, src, sizeof(*dst));
1412 AST_LIST_HEAD_INIT_NOLOCK(&dst->actions);
1414 AST_LIST_TRAVERSE(&src->actions, menu_action, action) {
1415 if (!(new_menu_action = ast_calloc(1, sizeof(*new_menu_action)))) {
1418 memcpy(new_menu_action, menu_action, sizeof(*new_menu_action));
1419 AST_LIST_INSERT_HEAD(&dst->actions, new_menu_action, action);
1424 void conf_menu_entry_destroy(struct conf_menu_entry *menu_entry)
1426 struct conf_menu_action *menu_action = NULL;
1427 while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
1428 ast_free(menu_action);
1432 int conf_find_menu_entry_by_sequence(const char *dtmf_sequence, struct conf_menu *menu, struct conf_menu_entry *result)
1434 struct conf_menu_entry *menu_entry = NULL;
1437 AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
1438 if (!strcasecmp(menu_entry->dtmf, dtmf_sequence)) {
1439 copy_menu_entry(result, menu_entry);
1449 int conf_set_menu_to_user(const char *menu_name, struct conference_bridge_user *conference_bridge_user)
1451 struct conf_menu tmp;
1452 struct conf_menu *menu;
1453 struct conf_menu_entry *menu_entry = NULL;
1454 ast_copy_string(tmp.name, menu_name, sizeof(tmp.name));
1456 if (!(menu = ao2_find(menus, &tmp, OBJ_POINTER))) {
1460 AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
1461 struct dtmf_menu_hook_pvt *pvt;
1462 if (!(pvt = ast_calloc(1, sizeof(*pvt)))) {
1467 if (copy_menu_entry(&pvt->menu_entry, menu_entry)) {
1473 pvt->conference_bridge_user = conference_bridge_user;
1477 ast_bridge_features_hook(&conference_bridge_user->features, pvt->menu_entry.dtmf, menu_hook_callback, pvt, menu_hook_destroy);