Fix configuring custom sound_leader_has_left in confbridge.conf
[asterisk/asterisk.git] / apps / confbridge / conf_config_parser.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2011, Digium, Inc.
5  *
6  * David Vossel <dvossel@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*! \file
20  *
21  * \brief ConfBridge config parser
22  *
23  * \author David Vossel <dvossel@digium.com>
24  */
25
26 #include "asterisk.h"
27
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"
37
38 #define CONFBRIDGE_CONFIG "confbridge.conf"
39
40 static struct ao2_container *user_profiles;
41 static struct ao2_container *bridge_profiles;
42 static struct ao2_container *menus;
43
44 /*! bridge profile container functions */
45 static int bridge_cmp_cb(void *obj, void *arg, int flags)
46 {
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;
50 }
51 static int bridge_hash_cb(const void *obj, const int flags)
52 {
53         const struct bridge_profile *b_profile = obj;
54         return ast_str_case_hash(b_profile->name);
55 }
56 static int bridge_mark_delme_cb(void *obj, void *arg, int flag)
57 {
58         struct bridge_profile *entry = obj;
59         entry->delme = 1;
60         return 0;
61 }
62 static int match_bridge_delme_cb(void *obj, void *arg, int flag)
63 {
64         const struct bridge_profile *entry = obj;
65         return entry->delme ? CMP_MATCH : 0;
66 }
67
68 /*! menu container functions */
69 static int menu_cmp_cb(void *obj, void *arg, int flags)
70 {
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;
74 }
75 static int menu_hash_cb(const void *obj, const int flags)
76 {
77         const struct conf_menu *menu = obj;
78         return ast_str_case_hash(menu->name);
79 }
80 static int menu_mark_delme_cb(void *obj, void *arg, int flag)
81 {
82         struct conf_menu *entry = obj;
83         entry->delme = 1;
84         return 0;
85 }
86 static int match_menu_delme_cb(void *obj, void *arg, int flag)
87 {
88         const struct conf_menu *entry = obj;
89         return entry->delme ? CMP_MATCH : 0;
90 }
91 static void menu_destructor(void *obj)
92 {
93         struct conf_menu *menu = obj;
94         struct conf_menu_entry *entry = NULL;
95
96         while ((entry = AST_LIST_REMOVE_HEAD(&menu->entries, entry))) {
97                 conf_menu_entry_destroy(entry);
98                 ast_free(entry);
99         }
100 }
101
102 /*! User profile container functions */
103 static int user_cmp_cb(void *obj, void *arg, int flags)
104 {
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;
108 }
109 static int user_hash_cb(const void *obj, const int flags)
110 {
111         const struct user_profile *u_profile = obj;
112         return ast_str_case_hash(u_profile->name);
113 }
114 static int user_mark_delme_cb(void *obj, void *arg, int flag)
115 {
116         struct user_profile *entry = obj;
117         entry->delme = 1;
118         return 0;
119 }
120 static int match_user_delme_cb(void *obj, void *arg, int flag)
121 {
122         const struct user_profile *entry = obj;
123         return entry->delme ? CMP_MATCH : 0;
124 }
125
126 /*! Bridge Profile Sounds functions */
127 static void bridge_profile_sounds_destroy_cb(void *obj)
128 {
129         struct bridge_profile_sounds *sounds = obj;
130         ast_string_field_free_memory(sounds);
131 }
132
133 static struct bridge_profile_sounds *bridge_profile_sounds_alloc(void)
134 {
135         struct bridge_profile_sounds *sounds = ao2_alloc(sizeof(*sounds), bridge_profile_sounds_destroy_cb);
136
137         if (!sounds) {
138                 return NULL;
139         }
140         if (ast_string_field_init(sounds, 512)) {
141                 ao2_ref(sounds, -1);
142                 return NULL;
143         }
144
145         return sounds;
146 }
147
148 static int set_user_option(const char *name, const char *value, struct user_profile *u_profile)
149 {
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;
167                 } else {
168                         return -1;
169                 }
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, "announcement")) {
191                 ast_copy_string(u_profile->announcement, value, sizeof(u_profile->announcement));
192         } else if (!strcasecmp(name, "denoise")) {
193                 ast_set2_flag(u_profile, ast_true(value), USER_OPT_DENOISE);
194         } else if (!strcasecmp(name, "dsp_talking_threshold")) {
195                 if (sscanf(value, "%30u", &u_profile->talking_threshold) != 1) {
196                         return -1;
197                 }
198         } else if (!strcasecmp(name, "dsp_silence_threshold")) {
199                 if (sscanf(value, "%30u", &u_profile->silence_threshold) != 1) {
200                         return -1;
201                 }
202         } else if (!strcasecmp(name, "dsp_drop_silence")) {
203                 ast_set2_flag(u_profile, ast_true(value), USER_OPT_DROP_SILENCE);
204         } else if (!strcasecmp(name, "template")) {
205                 if (!(conf_find_user_profile(NULL, value, u_profile))) {
206                         return -1;
207                 }
208         } else if (!strcasecmp(name, "jitterbuffer")) {
209                 ast_set2_flag(u_profile, ast_true(value), USER_OPT_JITTERBUFFER);
210         } else {
211                 return -1;
212         }
213         return 0;
214 }
215
216 static int set_sound(const char *sound_name, const char *sound_file, struct bridge_profile_sounds *sounds)
217 {
218         if (ast_strlen_zero(sound_file)) {
219                 return -1;
220         }
221
222         if (!strcasecmp(sound_name, "sound_only_person")) {
223                 ast_string_field_set(sounds, onlyperson, sound_file);
224         } else if (!strcasecmp(sound_name, "sound_only_one")) {
225                 ast_string_field_set(sounds, onlyone, sound_file);
226         } else if (!strcasecmp(sound_name, "sound_has_joined")) {
227                 ast_string_field_set(sounds, hasjoin, sound_file);
228         } else if (!strcasecmp(sound_name, "sound_has_left")) {
229                 ast_string_field_set(sounds, hasleft, sound_file);
230         } else if (!strcasecmp(sound_name, "sound_kicked")) {
231                 ast_string_field_set(sounds, kicked, sound_file);
232         } else if (!strcasecmp(sound_name, "sound_muted")) {
233                 ast_string_field_set(sounds, muted, sound_file);
234         } else if (!strcasecmp(sound_name, "sound_unmuted")) {
235                 ast_string_field_set(sounds, unmuted, sound_file);
236         } else if (!strcasecmp(sound_name, "sound_there_are")) {
237                 ast_string_field_set(sounds, thereare, sound_file);
238         } else if (!strcasecmp(sound_name, "sound_other_in_party")) {
239                 ast_string_field_set(sounds, otherinparty, sound_file);
240         } else if (!strcasecmp(sound_name, "sound_place_into_conference")) {
241                 ast_string_field_set(sounds, placeintoconf, sound_file);
242         } else if (!strcasecmp(sound_name, "sound_wait_for_leader")) {
243                 ast_string_field_set(sounds, waitforleader, sound_file);
244         } else if (!strcasecmp(sound_name, "sound_leader_has_left")) {
245                 ast_string_field_set(sounds, leaderhasleft, sound_file);
246         } else if (!strcasecmp(sound_name, "sound_get_pin")) {
247                 ast_string_field_set(sounds, getpin, sound_file);
248         } else if (!strcasecmp(sound_name, "sound_invalid_pin")) {
249                 ast_string_field_set(sounds, invalidpin, sound_file);
250         } else if (!strcasecmp(sound_name, "sound_locked")) {
251                 ast_string_field_set(sounds, locked, sound_file);
252         } else if (!strcasecmp(sound_name, "sound_unlocked_now")) {
253                 ast_string_field_set(sounds, unlockednow, sound_file);
254         } else if (!strcasecmp(sound_name, "sound_locked_now")) {
255                 ast_string_field_set(sounds, lockednow, sound_file);
256         } else if (!strcasecmp(sound_name, "sound_error_menu")) {
257                 ast_string_field_set(sounds, errormenu, sound_file);
258         } else if (!strcasecmp(sound_name, "sound_join")) {
259                 ast_string_field_set(sounds, join, sound_file);
260         } else if (!strcasecmp(sound_name, "sound_leave")) {
261                 ast_string_field_set(sounds, leave, sound_file);
262         } else if (!strcasecmp(sound_name, "sound_participants_muted")) {
263                 ast_string_field_set(sounds, participantsmuted, sound_file);
264         } else if (!strcasecmp(sound_name, "sound_participants_unmuted")) {
265                 ast_string_field_set(sounds, participantsunmuted, sound_file);
266         } else {
267                 return -1;
268         }
269
270         return 0;
271 }
272 static int set_bridge_option(const char *name, const char *value, struct bridge_profile *b_profile)
273 {
274         if (!strcasecmp(name, "internal_sample_rate")) {
275                 if (!strcasecmp(value, "auto")) {
276                         b_profile->internal_sample_rate = 0;
277                 } else if (sscanf(value, "%30u", &b_profile->internal_sample_rate) != 1) {
278                         return -1;
279                 }
280         } else if (!strcasecmp(name, "mixing_interval")) {
281                 if (sscanf(value, "%30u", &b_profile->mix_interval) != 1) {
282                         return -1;
283                 }
284                 switch (b_profile->mix_interval) {
285                 case 10:
286                 case 20:
287                 case 40:
288                 case 80:
289                         break;
290                 default:
291                         ast_log(LOG_WARNING, "invalid mixing interval %u\n", b_profile->mix_interval);
292                         b_profile->mix_interval = 0;
293                         return -1;
294                 }
295         } else if (!strcasecmp(name, "record_conference")) {
296                 ast_set2_flag(b_profile, ast_true(value), BRIDGE_OPT_RECORD_CONFERENCE);
297         } else if (!strcasecmp(name, "video_mode")) {
298                 if (!strcasecmp(value, "first_marked")) {
299                         ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED);
300                 } else if (!strcasecmp(value, "last_marked")) {
301                         ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED);
302                 } else if (!strcasecmp(value, "follow_talker")) {
303                         ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
304                 }
305         } else if (!strcasecmp(name, "max_members")) {
306                 if (sscanf(value, "%30u", &b_profile->max_members) != 1) {
307                         return -1;
308                 }
309         } else if (!strcasecmp(name, "record_file")) {
310                 ast_copy_string(b_profile->rec_file, value, sizeof(b_profile->rec_file));
311         } else if (strlen(name) >= 5 && !strncasecmp(name, "sound", 5)) {
312                 if (set_sound(name, value, b_profile->sounds)) {
313                         return -1;
314                 }
315         } else if (!strcasecmp(name, "template")) { /* Only documented for use in CONFBRIDGE dialplan function */
316                 struct bridge_profile *tmp = b_profile;
317                 struct bridge_profile_sounds *sounds = bridge_profile_sounds_alloc();
318                 struct bridge_profile_sounds *oldsounds = b_profile->sounds;
319                 if (!sounds) {
320                         return -1;
321                 }
322                 if (!(conf_find_bridge_profile(NULL, value, tmp))) {
323                         ao2_ref(sounds, -1);
324                         return -1;
325                 }
326                 /* Using a bridge profile as a template is a little complicated due to the sounds. Since the sounds
327                  * structure of a dynamic profile will need to be altered, a completely new sounds structure must be
328                  * created instead of simply holding a reference to the one built by the config file. */
329                 ast_string_field_set(sounds, onlyperson, tmp->sounds->onlyperson);
330                 ast_string_field_set(sounds, hasjoin, tmp->sounds->hasjoin);
331                 ast_string_field_set(sounds, hasleft, tmp->sounds->hasleft);
332                 ast_string_field_set(sounds, kicked, tmp->sounds->kicked);
333                 ast_string_field_set(sounds, muted, tmp->sounds->muted);
334                 ast_string_field_set(sounds, unmuted, tmp->sounds->unmuted);
335                 ast_string_field_set(sounds, thereare, tmp->sounds->thereare);
336                 ast_string_field_set(sounds, otherinparty, tmp->sounds->otherinparty);
337                 ast_string_field_set(sounds, placeintoconf, tmp->sounds->placeintoconf);
338                 ast_string_field_set(sounds, waitforleader, tmp->sounds->waitforleader);
339                 ast_string_field_set(sounds, leaderhasleft, tmp->sounds->leaderhasleft);
340                 ast_string_field_set(sounds, getpin, tmp->sounds->getpin);
341                 ast_string_field_set(sounds, invalidpin, tmp->sounds->invalidpin);
342                 ast_string_field_set(sounds, locked, tmp->sounds->locked);
343                 ast_string_field_set(sounds, unlockednow, tmp->sounds->unlockednow);
344                 ast_string_field_set(sounds, lockednow, tmp->sounds->lockednow);
345                 ast_string_field_set(sounds, errormenu, tmp->sounds->errormenu);
346                 ast_string_field_set(sounds, participantsmuted, tmp->sounds->participantsmuted);
347                 ast_string_field_set(sounds, participantsunmuted, tmp->sounds->participantsunmuted);
348
349                 ao2_ref(tmp->sounds, -1); /* sounds struct copied over to it from the template by reference only. */
350                 ao2_ref(oldsounds,-1);    /* original sounds struct we don't need anymore */
351                 tmp->sounds = sounds;     /* the new sounds struct that is a deep copy of the one from the template. */
352         } else {
353                 return -1;
354         }
355
356         return 0;
357 }
358
359 /*! CONFBRIDGE dialplan function functions and channel datastore. */
360 struct func_confbridge_data {
361         struct bridge_profile b_profile;
362         struct user_profile u_profile;
363         unsigned int b_usable:1; /*!< Tells if bridge profile is usable or not */
364         unsigned int u_usable:1; /*!< Tells if user profile is usable or not */
365 };
366 static void func_confbridge_destroy_cb(void *data)
367 {
368         struct func_confbridge_data *b_data = data;
369         conf_bridge_profile_destroy(&b_data->b_profile);
370         ast_free(b_data);
371 };
372 static const struct ast_datastore_info confbridge_datastore = {
373         .type = "confbridge",
374         .destroy = func_confbridge_destroy_cb
375 };
376 int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value)
377 {
378         struct ast_datastore *datastore = NULL;
379         struct func_confbridge_data *b_data = NULL;
380         char *parse = NULL;
381         int new = 0;
382         AST_DECLARE_APP_ARGS(args,
383                 AST_APP_ARG(type);
384                 AST_APP_ARG(option);
385         );
386
387         /* parse all the required arguments and make sure they exist. */
388         if (ast_strlen_zero(data) || ast_strlen_zero(value)) {
389                 return -1;
390         }
391         parse = ast_strdupa(data);
392         AST_STANDARD_APP_ARGS(args, parse);
393         if (ast_strlen_zero(args.type) || ast_strlen_zero(args.option)) {
394                 return -1;
395         }
396
397         ast_channel_lock(chan);
398         if (!(datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL))) {
399                 ast_channel_unlock(chan);
400
401                 if (!(datastore = ast_datastore_alloc(&confbridge_datastore, NULL))) {
402                         return 0;
403                 }
404                 if (!(b_data = ast_calloc(1, sizeof(*b_data)))) {
405                         ast_datastore_free(datastore);
406                         return 0;
407                 }
408                 if (!(b_data->b_profile.sounds = bridge_profile_sounds_alloc())) {
409                         ast_datastore_free(datastore);
410                         ast_free(b_data);
411                         return 0;
412                 }
413                 datastore->data = b_data;
414                 new = 1;
415         } else {
416                 ast_channel_unlock(chan);
417                 b_data = datastore->data;
418         }
419
420         /* SET(CONFBRIDGE(type,option)=value) */
421         if (!strcasecmp(args.type, "bridge") && !set_bridge_option(args.option, value, &b_data->b_profile)) {
422                 b_data->b_usable = 1;
423         } else if (!strcasecmp(args.type, "user") && !set_user_option(args.option, value, &b_data->u_profile)) {
424                 b_data->u_usable = 1;
425         } else {
426                 ast_log(LOG_WARNING, "Profile type \"%s\" can not be set in CONFBRIDGE function with option \"%s\" and value \"%s\"\n",
427                         args.type, args.option, value);
428                 goto cleanup_error;
429         }
430         if (new) {
431                 ast_channel_lock(chan);
432                 ast_channel_datastore_add(chan, datastore);
433                 ast_channel_unlock(chan);
434         }
435         return 0;
436
437 cleanup_error:
438         ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
439         if (new) {
440                 ast_datastore_free(datastore);
441         }
442         return -1;
443 }
444
445 /*!
446  * \brief Parse the bridge profile options
447  */
448 static int parse_bridge(const char *cat, struct ast_config *cfg)
449 {
450         struct ast_variable *var;
451         struct bridge_profile tmp;
452         struct bridge_profile *b_profile;
453
454         ast_copy_string(tmp.name, cat, sizeof(tmp.name));
455         if ((b_profile = ao2_find(bridge_profiles, &tmp, OBJ_POINTER))) {
456                 b_profile->delme = 0;
457         } else if ((b_profile = ao2_alloc(sizeof(*b_profile), NULL))) {
458                 ast_copy_string(b_profile->name, cat, sizeof(b_profile->name));
459                 ao2_link(bridge_profiles, b_profile);
460         } else {
461                 return -1;
462         }
463
464         ao2_lock(b_profile);
465         /* set defaults */
466         b_profile->internal_sample_rate = 0;
467         b_profile->flags = 0;
468         b_profile->max_members = 0;
469         b_profile->mix_interval = 0;
470         memset(b_profile->rec_file, 0, sizeof(b_profile->rec_file));
471         if (b_profile->sounds) {
472                 ao2_ref(b_profile->sounds, -1); /* sounds is read only.  Once it has been created
473                                                  * it can never be altered. This prevents having to
474                                                  * do any locking after it is built from the config. */
475                 b_profile->sounds = NULL;
476         }
477
478         if (!(b_profile->sounds = bridge_profile_sounds_alloc())) {
479                 ao2_unlock(b_profile);
480                 ao2_ref(b_profile, -1);
481                 ao2_unlink(bridge_profiles, b_profile);
482                 return -1;
483         }
484
485         for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
486                 if (!strcasecmp(var->name, "type")) {
487                         continue;
488                 } else if (set_bridge_option(var->name, var->value, b_profile)) {
489                         ast_log(LOG_WARNING, "Invalid: '%s' at line %d of %s is not supported.\n",
490                                 var->name, var->lineno, CONFBRIDGE_CONFIG);
491                 }
492         }
493         ao2_unlock(b_profile);
494
495         ao2_ref(b_profile, -1);
496         return 0;
497 }
498
499 static int parse_user(const char *cat, struct ast_config *cfg)
500 {
501         struct ast_variable *var;
502         struct user_profile tmp;
503         struct user_profile *u_profile;
504
505         ast_copy_string(tmp.name, cat, sizeof(tmp.name));
506         if ((u_profile = ao2_find(user_profiles, &tmp, OBJ_POINTER))) {
507                 u_profile->delme = 0;
508         } else if ((u_profile = ao2_alloc(sizeof(*u_profile), NULL))) {
509                 ast_copy_string(u_profile->name, cat, sizeof(u_profile->name));
510                 ao2_link(user_profiles, u_profile);
511         } else {
512                 return -1;
513         }
514
515         ao2_lock(u_profile);
516         /* set defaults */
517         u_profile->flags = 0;
518         u_profile->announce_user_count_all_after = 0;
519         u_profile->silence_threshold = DEFAULT_SILENCE_THRESHOLD;
520         u_profile->talking_threshold = DEFAULT_TALKING_THRESHOLD;
521         memset(u_profile->pin, 0, sizeof(u_profile->pin));
522         memset(u_profile->moh_class, 0, sizeof(u_profile->moh_class));
523         memset(u_profile->announcement, 0, sizeof(u_profile->announcement));
524         for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
525                 if (!strcasecmp(var->name, "type")) {
526                         continue;
527                 } else if (set_user_option(var->name, var->value, u_profile)) {
528                         ast_log(LOG_WARNING, "Invalid option '%s' at line %d of %s is not supported.\n",
529                                 var->name, var->lineno, CONFBRIDGE_CONFIG);
530                 }
531         }
532         ao2_unlock(u_profile);
533
534         ao2_ref(u_profile, -1);
535         return 0;
536 }
537
538 static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum conf_menu_action_id id, char *databuf)
539 {
540         struct conf_menu_action *menu_action = ast_calloc(1, sizeof(*menu_action));
541
542         if (!menu_action) {
543                 return -1;
544         }
545         menu_action->id = id;
546
547         switch (id) {
548         case MENU_ACTION_NOOP:
549         case MENU_ACTION_TOGGLE_MUTE:
550         case MENU_ACTION_INCREASE_LISTENING:
551         case MENU_ACTION_DECREASE_LISTENING:
552         case MENU_ACTION_INCREASE_TALKING:
553         case MENU_ACTION_DECREASE_TALKING:
554         case MENU_ACTION_RESET_LISTENING:
555         case MENU_ACTION_RESET_TALKING:
556         case MENU_ACTION_ADMIN_TOGGLE_LOCK:
557         case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
558         case MENU_ACTION_PARTICIPANT_COUNT:
559         case MENU_ACTION_ADMIN_KICK_LAST:
560         case MENU_ACTION_LEAVE:
561         case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
562         case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
563                 break;
564         case MENU_ACTION_PLAYBACK:
565         case MENU_ACTION_PLAYBACK_AND_CONTINUE:
566                 if (!(ast_strlen_zero(databuf))) {
567                         ast_copy_string(menu_action->data.playback_file, databuf, sizeof(menu_action->data.playback_file));
568                 } else {
569                         ast_free(menu_action);
570                         return -1;
571                 }
572                 break;
573         case MENU_ACTION_DIALPLAN_EXEC:
574                 if (!(ast_strlen_zero(databuf))) {
575                         AST_DECLARE_APP_ARGS(args,
576                                 AST_APP_ARG(context);
577                                 AST_APP_ARG(exten);
578                                 AST_APP_ARG(priority);
579                         );
580                         AST_STANDARD_APP_ARGS(args, databuf);
581                         if (!ast_strlen_zero(args.context)) {
582                                 ast_copy_string(menu_action->data.dialplan_args.context,
583                                         args.context,
584                                         sizeof(menu_action->data.dialplan_args.context));
585                         }
586                         if (!ast_strlen_zero(args.exten)) {
587                                 ast_copy_string(menu_action->data.dialplan_args.exten,
588                                         args.exten,
589                                         sizeof(menu_action->data.dialplan_args.exten));
590                         }
591                         menu_action->data.dialplan_args.priority = 1; /* 1 by default */
592                         if (!ast_strlen_zero(args.priority) &&
593                                 (sscanf(args.priority, "%30u", &menu_action->data.dialplan_args.priority) != 1)) {
594                                 /* invalid priority */
595                                 ast_free(menu_action);
596                                 return -1;
597                         }
598                 } else {
599                         ast_free(menu_action);
600                         return -1;
601                 }
602         };
603
604         AST_LIST_INSERT_TAIL(&menu_entry->actions, menu_action, action);
605
606         return 0;
607 }
608
609 static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *action_names)
610 {
611         struct conf_menu_entry *menu_entry = NULL, *cur = NULL;
612         int res = 0;
613         char *tmp_action_names = ast_strdupa(action_names);
614         char *action = NULL;
615         char *action_args;
616         char *tmp;
617         char buf[PATH_MAX];
618         char *delimiter = ",";
619
620         if (!(menu_entry = ast_calloc(1, sizeof(*menu_entry)))) {
621                 return -1;
622         }
623
624         for (;;) {
625                 char *comma;
626                 char *startbrace;
627                 char *endbrace;
628                 unsigned int action_len;
629
630                 if (ast_strlen_zero(tmp_action_names)) {
631                         break;
632                 }
633                 startbrace = strchr(tmp_action_names, '(');
634                 endbrace = strchr(tmp_action_names, ')');
635                 comma = strchr(tmp_action_names, ',');
636
637                 /* If the next action has brackets with comma delimited arguments in it,
638                  * make the delimeter ')' instead of a comma to preserve the argments */
639                 if (startbrace && endbrace && comma && (comma > startbrace && comma < endbrace)) {
640                         delimiter = ")";
641                 } else {
642                         delimiter = ",";
643                 }
644
645                 if (!(action = strsep(&tmp_action_names, delimiter))) {
646                         break;
647                 }
648
649                 action = ast_strip(action);
650                 if (ast_strlen_zero(action)) {
651                         continue;
652                 }
653
654                 action_len = strlen(action);
655                 ast_copy_string(menu_entry->dtmf, dtmf, sizeof(menu_entry->dtmf));
656                 if (!strcasecmp(action, "toggle_mute")) {
657                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_TOGGLE_MUTE, NULL);
658                 } else if (!strcasecmp(action, "no_op")) {
659                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_NOOP, NULL);
660                 } else if (!strcasecmp(action, "increase_listening_volume")) {
661                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_LISTENING, NULL);
662                 } else if (!strcasecmp(action, "decrease_listening_volume")) {
663                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_LISTENING, NULL);
664                 } else if (!strcasecmp(action, "increase_talking_volume")) {
665                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_TALKING, NULL);
666                 } else if (!strcasecmp(action, "reset_listening_volume")) {
667                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_LISTENING, NULL);
668                 } else if (!strcasecmp(action, "reset_talking_volume")) {
669                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_TALKING, NULL);
670                 } else if (!strcasecmp(action, "decrease_talking_volume")) {
671                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_TALKING, NULL);
672                 } else if (!strcasecmp(action, "admin_toggle_conference_lock")) {
673                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_LOCK, NULL);
674                 } else if (!strcasecmp(action, "admin_toggle_mute_participants")) {
675                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS, NULL);
676                 } else if (!strcasecmp(action, "participant_count")) {
677                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PARTICIPANT_COUNT, NULL);
678                 } else if (!strcasecmp(action, "admin_kick_last")) {
679                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_KICK_LAST, NULL);
680                 } else if (!strcasecmp(action, "leave_conference")) {
681                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_LEAVE, NULL);
682                 } else if (!strcasecmp(action, "set_as_single_video_src")) {
683                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_SET_SINGLE_VIDEO_SRC, NULL);
684                 } else if (!strcasecmp(action, "release_as_single_video_src")) {
685                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC, NULL);
686                 } else if (!strncasecmp(action, "dialplan_exec(", 14)) {
687                         ast_copy_string(buf, action, sizeof(buf));
688                         action_args = buf;
689                         if ((action_args = strchr(action, '('))) {
690                                 action_args++;
691                         }
692                         /* it is possible that this argument may or may not
693                          * have a closing brace at this point, it all depends on if
694                          * comma delimited arguments were provided */
695                         if ((tmp = strchr(action, ')'))) {
696                                 *tmp = '\0';
697                         }
698                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DIALPLAN_EXEC, action_args);
699                 } else if (action_len >= 21 && !strncasecmp(action, "playback_and_continue(", 22)) {
700                         ast_copy_string(buf, action, sizeof(buf));
701                         action_args = buf;
702                         if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
703                                 *tmp = '\0';
704                                 action_args++;
705                         }
706                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK_AND_CONTINUE, action_args);
707                 } else if (action_len >= 8 && !strncasecmp(action, "playback(", 9)) {
708                         ast_copy_string(buf, action, sizeof(buf));
709                         action_args = buf;
710                         if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
711                                 *tmp = '\0';
712                                 action_args++;
713                         }
714                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK, action_args);
715                 }
716         }
717
718         /* if adding any of the actions failed, bail */
719         if (res) {
720                 struct conf_menu_action *menu_action;
721                 while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
722                         ast_free(menu_action);
723                 }
724                 ast_free(menu_entry);
725                 return -1;
726         }
727
728         /* remove any list entry with an identical DTMF sequence for overrides */
729         AST_LIST_TRAVERSE_SAFE_BEGIN(&menu->entries, cur, entry) {
730                 if (!strcasecmp(cur->dtmf, menu_entry->dtmf)) {
731                         AST_LIST_REMOVE_CURRENT(entry);
732                         ast_free(cur);
733                         break;
734                 }
735         }
736         AST_LIST_TRAVERSE_SAFE_END;
737
738         AST_LIST_INSERT_TAIL(&menu->entries, menu_entry, entry);
739
740         return 0;
741 }
742 static int parse_menu(const char *cat, struct ast_config *cfg)
743 {
744         struct ast_variable *var;
745         struct conf_menu tmp;
746         struct conf_menu *menu;
747
748         ast_copy_string(tmp.name, cat, sizeof(tmp.name));
749         if ((menu = ao2_find(menus, &tmp, OBJ_POINTER))) {
750                 menu->delme = 0;
751         } else if ((menu = ao2_alloc(sizeof(*menu), menu_destructor))) {
752                 ast_copy_string(menu->name, cat, sizeof(menu->name));
753                 ao2_link(menus, menu);
754         } else {
755                 return -1;
756         }
757
758         ao2_lock(menu);
759         /* this isn't freeing the menu, just destroying the menu list so it can be rebuilt.*/
760         menu_destructor(menu);
761         for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
762                 if (!strcasecmp(var->name, "type")) {
763                         continue;
764                 } else if (add_menu_entry(menu, var->name, var->value)) {
765                         ast_log(LOG_WARNING, "Unknown option '%s' at line %d of %s is not supported.\n",
766                                 var->name, var->lineno, CONFBRIDGE_CONFIG);
767                 }
768         }
769         ao2_unlock(menu);
770
771         ao2_ref(menu, -1);
772         return 0;
773 }
774
775 static char *complete_user_profile_name(const char *line, const char *word, int pos, int state)
776 {
777         int which = 0;
778         char *res = NULL;
779         int wordlen = strlen(word);
780         struct ao2_iterator i;
781         struct user_profile *u_profile = NULL;
782
783         i = ao2_iterator_init(user_profiles, 0);
784         while ((u_profile = ao2_iterator_next(&i))) {
785                 if (!strncasecmp(u_profile->name, word, wordlen) && ++which > state) {
786                         res = ast_strdup(u_profile->name);
787                         ao2_ref(u_profile, -1);
788                         break;
789                 }
790                 ao2_ref(u_profile, -1);
791         }
792         ao2_iterator_destroy(&i);
793
794         return res;
795 }
796
797 static char *handle_cli_confbridge_show_user_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
798 {
799         struct ao2_iterator it;
800         struct user_profile *u_profile;
801
802         switch (cmd) {
803         case CLI_INIT:
804                 e->command = "confbridge show profile users";
805                 e->usage =
806                         "Usage confbridge show profile users\n";
807                 return NULL;
808         case CLI_GENERATE:
809                 return NULL;
810         }
811
812         ast_cli(a->fd,"--------- User Profiles -----------\n");
813         ao2_lock(user_profiles);
814         it = ao2_iterator_init(user_profiles, 0);
815         while ((u_profile = ao2_iterator_next(&it))) {
816                 ast_cli(a->fd,"%s\n", u_profile->name);
817                 ao2_ref(u_profile, -1);
818         }
819         ao2_iterator_destroy(&it);
820         ao2_unlock(user_profiles);
821
822         return CLI_SUCCESS;
823 }
824 static char *handle_cli_confbridge_show_user_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
825 {
826         struct user_profile u_profile;
827
828         switch (cmd) {
829         case CLI_INIT:
830                 e->command = "confbridge show profile user";
831                 e->usage =
832                         "Usage confbridge show profile user [<profile name>]\n";
833                 return NULL;
834         case CLI_GENERATE:
835                 if (a->pos == 4) {
836                         return complete_user_profile_name(a->line, a->word, a->pos, a->n);
837                 }
838                 return NULL;
839         }
840
841         if (a->argc != 5) {
842                 return CLI_SHOWUSAGE;
843         }
844
845         if (!(conf_find_user_profile(NULL, a->argv[4], &u_profile))) {
846                 ast_cli(a->fd, "No conference user profile named '%s' found!\n", a->argv[4]);
847                 return CLI_SUCCESS;
848         }
849
850         ast_cli(a->fd,"--------------------------------------------\n");
851         ast_cli(a->fd,"Name:                    %s\n",
852                 u_profile.name);
853         ast_cli(a->fd,"Admin:                   %s\n",
854                 u_profile.flags & USER_OPT_ADMIN ?
855                 "true" : "false");
856         ast_cli(a->fd,"Marked User:             %s\n",
857                 u_profile.flags & USER_OPT_MARKEDUSER ?
858                 "true" : "false");
859         ast_cli(a->fd,"Start Muted:             %s\n",
860                 u_profile.flags & USER_OPT_STARTMUTED?
861                 "true" : "false");
862         ast_cli(a->fd,"MOH When Empty:          %s\n",
863                 u_profile.flags & USER_OPT_MUSICONHOLD ?
864                 "enabled" : "disabled");
865         ast_cli(a->fd,"MOH Class:               %s\n",
866                 ast_strlen_zero(u_profile.moh_class) ?
867                 "default" : u_profile.moh_class);
868         ast_cli(a->fd,"Announcement:            %s\n",
869                 u_profile.announcement);
870         ast_cli(a->fd,"Quiet:                   %s\n",
871                 u_profile.flags & USER_OPT_QUIET ?
872                 "enabled" : "disabled");
873         ast_cli(a->fd,"Wait Marked:             %s\n",
874                 u_profile.flags & USER_OPT_WAITMARKED ?
875                 "enabled" : "disabled");
876         ast_cli(a->fd,"END Marked:              %s\n",
877                 u_profile.flags & USER_OPT_ENDMARKED ?
878                 "enabled" : "disabled");
879         ast_cli(a->fd,"Drop_silence:            %s\n",
880                 u_profile.flags & USER_OPT_DROP_SILENCE ?
881                 "enabled" : "disabled");
882         ast_cli(a->fd,"Silence Threshold:       %dms\n",
883                 u_profile.silence_threshold);
884         ast_cli(a->fd,"Talking Threshold:       %dms\n",
885                 u_profile.talking_threshold);
886         ast_cli(a->fd,"Denoise:                 %s\n",
887                 u_profile.flags & USER_OPT_DENOISE ?
888                 "enabled" : "disabled");
889         ast_cli(a->fd,"Jitterbuffer:            %s\n",
890                 u_profile.flags & USER_OPT_JITTERBUFFER ?
891                 "enabled" : "disabled");
892         ast_cli(a->fd,"Talk Detect Events:      %s\n",
893                 u_profile.flags & USER_OPT_TALKER_DETECT ?
894                 "enabled" : "disabled");
895         ast_cli(a->fd,"DTMF Pass Through:       %s\n",
896                 u_profile.flags & USER_OPT_DTMF_PASS ?
897                 "enabled" : "disabled");
898         ast_cli(a->fd,"PIN:                     %s\n",
899                 ast_strlen_zero(u_profile.pin) ?
900                 "None" : u_profile.pin);
901         ast_cli(a->fd,"Announce User Count:     %s\n",
902                 u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNT ?
903                 "enabled" : "disabled");
904         ast_cli(a->fd,"Announce join/leave:     %s\n",
905                 u_profile.flags & USER_OPT_ANNOUNCE_JOIN_LEAVE ?
906                 "enabled" : "disabled");
907         ast_cli(a->fd,"Announce User Count all: %s\n",
908                 u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNTALL ?
909                 "enabled" : "disabled");
910                 ast_cli(a->fd,"\n");
911
912         return CLI_SUCCESS;
913 }
914
915 static char *complete_bridge_profile_name(const char *line, const char *word, int pos, int state)
916 {
917         int which = 0;
918         char *res = NULL;
919         int wordlen = strlen(word);
920         struct ao2_iterator i;
921         struct bridge_profile *b_profile = NULL;
922
923         i = ao2_iterator_init(bridge_profiles, 0);
924         while ((b_profile = ao2_iterator_next(&i))) {
925                 if (!strncasecmp(b_profile->name, word, wordlen) && ++which > state) {
926                         res = ast_strdup(b_profile->name);
927                         ao2_ref(b_profile, -1);
928                         break;
929                 }
930                 ao2_ref(b_profile, -1);
931         }
932         ao2_iterator_destroy(&i);
933
934         return res;
935 }
936
937 static char *handle_cli_confbridge_show_bridge_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
938 {
939         struct ao2_iterator it;
940         struct bridge_profile *b_profile;
941
942         switch (cmd) {
943         case CLI_INIT:
944                 e->command = "confbridge show profile bridges";
945                 e->usage =
946                         "Usage confbridge show profile bridges\n";
947                 return NULL;
948         case CLI_GENERATE:
949                 return NULL;
950         }
951
952         ast_cli(a->fd,"--------- Bridge Profiles -----------\n");
953         ao2_lock(bridge_profiles);
954         it = ao2_iterator_init(bridge_profiles, 0);
955         while ((b_profile = ao2_iterator_next(&it))) {
956                 ast_cli(a->fd,"%s\n", b_profile->name);
957                 ao2_ref(b_profile, -1);
958         }
959         ao2_iterator_destroy(&it);
960         ao2_unlock(bridge_profiles);
961
962         return CLI_SUCCESS;
963 }
964
965 static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
966 {
967         struct bridge_profile b_profile;
968         char tmp[64];
969
970         switch (cmd) {
971         case CLI_INIT:
972                 e->command = "confbridge show profile bridge";
973                 e->usage =
974                         "Usage confbridge show profile bridge <profile name>\n";
975                 return NULL;
976         case CLI_GENERATE:
977                 if (a->pos == 4) {
978                         return complete_bridge_profile_name(a->line, a->word, a->pos, a->n);
979                 }
980                 return NULL;
981         }
982
983         if (a->argc != 5) {
984                 return CLI_SHOWUSAGE;
985         }
986
987         if (!(conf_find_bridge_profile(NULL, a->argv[4], &b_profile))) {
988                 ast_cli(a->fd, "No conference bridge profile named '%s' found!\n", a->argv[4]);
989                 return CLI_SUCCESS;
990         }
991
992         ast_cli(a->fd,"--------------------------------------------\n");
993         ast_cli(a->fd,"Name:                 %s\n", b_profile.name);
994
995         if (b_profile.internal_sample_rate) {
996                 snprintf(tmp, sizeof(tmp), "%d", b_profile.internal_sample_rate);
997         } else {
998                 ast_copy_string(tmp, "auto", sizeof(tmp));
999         }
1000         ast_cli(a->fd,"Internal Sample Rate: %s\n", tmp);
1001
1002         if (b_profile.mix_interval) {
1003                 ast_cli(a->fd,"Mixing Interval:      %d\n", b_profile.mix_interval);
1004         } else {
1005                 ast_cli(a->fd,"Mixing Interval:      Default 20ms\n");
1006         }
1007
1008         ast_cli(a->fd,"Record Conference:    %s\n",
1009                 b_profile.flags & BRIDGE_OPT_RECORD_CONFERENCE ?
1010                 "yes" : "no");
1011
1012         ast_cli(a->fd,"Record File:          %s\n",
1013                 ast_strlen_zero(b_profile.rec_file) ? "Auto Generated" :
1014                 b_profile.rec_file);
1015
1016         if (b_profile.max_members) {
1017                 ast_cli(a->fd,"Max Members:          %d\n", b_profile.max_members);
1018         } else {
1019                 ast_cli(a->fd,"Max Members:          No Limit\n");
1020         }
1021
1022         if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_LAST_MARKED) {
1023                 ast_cli(a->fd, "Video Mode:           last_marked\n");
1024         } else if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) {
1025                 ast_cli(a->fd, "Video Mode:           first_marked\n");
1026         } else if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER) {
1027                 ast_cli(a->fd, "Video Mode:           follow_talker\n");
1028         } else {
1029                 ast_cli(a->fd, "Video Mode:           no video\n");
1030         }
1031
1032         ast_cli(a->fd,"sound_join:           %s\n", conf_get_sound(CONF_SOUND_JOIN, b_profile.sounds));
1033         ast_cli(a->fd,"sound_leave:          %s\n", conf_get_sound(CONF_SOUND_LEAVE, b_profile.sounds));
1034         ast_cli(a->fd,"sound_only_person:    %s\n", conf_get_sound(CONF_SOUND_ONLY_PERSON, b_profile.sounds));
1035         ast_cli(a->fd,"sound_has_joined:     %s\n", conf_get_sound(CONF_SOUND_HAS_JOINED, b_profile.sounds));
1036         ast_cli(a->fd,"sound_has_left:       %s\n", conf_get_sound(CONF_SOUND_HAS_LEFT, b_profile.sounds));
1037         ast_cli(a->fd,"sound_kicked:         %s\n", conf_get_sound(CONF_SOUND_KICKED, b_profile.sounds));
1038         ast_cli(a->fd,"sound_muted:          %s\n", conf_get_sound(CONF_SOUND_MUTED, b_profile.sounds));
1039         ast_cli(a->fd,"sound_unmuted:        %s\n", conf_get_sound(CONF_SOUND_UNMUTED, b_profile.sounds));
1040         ast_cli(a->fd,"sound_there_are:      %s\n", conf_get_sound(CONF_SOUND_THERE_ARE, b_profile.sounds));
1041         ast_cli(a->fd,"sound_other_in_party: %s\n", conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, b_profile.sounds));
1042         ast_cli(a->fd,"sound_place_into_conference: %s\n", conf_get_sound(CONF_SOUND_PLACE_IN_CONF, b_profile.sounds));
1043         ast_cli(a->fd,"sound_wait_for_leader:       %s\n", conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, b_profile.sounds));
1044         ast_cli(a->fd,"sound_leader_has_left:       %s\n", conf_get_sound(CONF_SOUND_LEADER_HAS_LEFT, b_profile.sounds));
1045         ast_cli(a->fd,"sound_get_pin:        %s\n", conf_get_sound(CONF_SOUND_GET_PIN, b_profile.sounds));
1046         ast_cli(a->fd,"sound_invalid_pin:    %s\n", conf_get_sound(CONF_SOUND_INVALID_PIN, b_profile.sounds));
1047         ast_cli(a->fd,"sound_locked:         %s\n", conf_get_sound(CONF_SOUND_LOCKED, b_profile.sounds));
1048         ast_cli(a->fd,"sound_unlocked_now:   %s\n", conf_get_sound(CONF_SOUND_UNLOCKED_NOW, b_profile.sounds));
1049         ast_cli(a->fd,"sound_lockednow:      %s\n", conf_get_sound(CONF_SOUND_LOCKED_NOW, b_profile.sounds));
1050         ast_cli(a->fd,"sound_error_menu:     %s\n", conf_get_sound(CONF_SOUND_ERROR_MENU, b_profile.sounds));
1051         ast_cli(a->fd,"sound_participants_muted:     %s\n", conf_get_sound(CONF_SOUND_PARTICIPANTS_MUTED, b_profile.sounds));
1052         ast_cli(a->fd,"sound_participants_unmuted:     %s\n", conf_get_sound(CONF_SOUND_PARTICIPANTS_UNMUTED, b_profile.sounds));
1053         ast_cli(a->fd,"\n");
1054
1055         conf_bridge_profile_destroy(&b_profile);
1056         return CLI_SUCCESS;
1057 }
1058
1059 static char *complete_menu_name(const char *line, const char *word, int pos, int state)
1060 {
1061         int which = 0;
1062         char *res = NULL;
1063         int wordlen = strlen(word);
1064         struct ao2_iterator i;
1065         struct conf_menu *menu = NULL;
1066
1067         i = ao2_iterator_init(menus, 0);
1068         while ((menu = ao2_iterator_next(&i))) {
1069                 if (!strncasecmp(menu->name, word, wordlen) && ++which > state) {
1070                         res = ast_strdup(menu->name);
1071                         ao2_ref(menu, -1);
1072                         break;
1073                 }
1074                 ao2_ref(menu, -1);
1075         }
1076         ao2_iterator_destroy(&i);
1077
1078         return res;
1079 }
1080
1081 static char *handle_cli_confbridge_show_menus(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1082 {
1083         struct ao2_iterator it;
1084         struct conf_menu *menu;
1085
1086         switch (cmd) {
1087         case CLI_INIT:
1088                 e->command = "confbridge show menus";
1089                 e->usage =
1090                         "Usage confbridge show profile menus\n";
1091                 return NULL;
1092         case CLI_GENERATE:
1093                 return NULL;
1094         }
1095
1096         ast_cli(a->fd,"--------- Menus -----------\n");
1097         ao2_lock(menus);
1098         it = ao2_iterator_init(menus, 0);
1099         while ((menu = ao2_iterator_next(&it))) {
1100                 ast_cli(a->fd,"%s\n", menu->name);
1101                 ao2_ref(menu, -1);
1102         }
1103         ao2_iterator_destroy(&it);
1104         ao2_unlock(menus);
1105
1106         return CLI_SUCCESS;
1107 }
1108
1109 static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1110 {
1111         struct conf_menu tmp;
1112         struct conf_menu *menu;
1113         struct conf_menu_entry *menu_entry = NULL;
1114         struct conf_menu_action *menu_action = NULL;
1115
1116         switch (cmd) {
1117         case CLI_INIT:
1118                 e->command = "confbridge show menu";
1119                 e->usage =
1120                         "Usage confbridge show menu [<menu name>]\n";
1121                 return NULL;
1122         case CLI_GENERATE:
1123                 if (a->pos == 3) {
1124                         return complete_menu_name(a->line, a->word, a->pos, a->n);
1125                 }
1126                 return NULL;
1127         }
1128
1129         if (a->argc != 4) {
1130                 return CLI_SHOWUSAGE;
1131         }
1132
1133         ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
1134         if (!(menu = ao2_find(menus, &tmp, OBJ_POINTER))) {
1135                 ast_cli(a->fd, "No conference menu named '%s' found!\n", a->argv[3]);
1136                 return CLI_SUCCESS;
1137         }
1138         ao2_lock(menu);
1139
1140         ast_cli(a->fd,"Name: %s\n", menu->name);
1141         AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
1142                 int action_num = 0;
1143                 ast_cli(a->fd, "%s=", menu_entry->dtmf);
1144                 AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
1145                         if (action_num) {
1146                                 ast_cli(a->fd, ", ");
1147                         }
1148                         switch (menu_action->id) {
1149                         case MENU_ACTION_TOGGLE_MUTE:
1150                                 ast_cli(a->fd, "toggle_mute");
1151                                 break;
1152                         case MENU_ACTION_NOOP:
1153                                 ast_cli(a->fd, "no_op");
1154                                 break;
1155                         case MENU_ACTION_INCREASE_LISTENING:
1156                                 ast_cli(a->fd, "increase_listening_volume");
1157                                 break;
1158                         case MENU_ACTION_DECREASE_LISTENING:
1159                                 ast_cli(a->fd, "decrease_listening_volume");
1160                                 break;
1161                         case MENU_ACTION_RESET_LISTENING:
1162                                 ast_cli(a->fd, "reset_listening_volume");
1163                                 break;
1164                         case MENU_ACTION_RESET_TALKING:
1165                                 ast_cli(a->fd, "reset_talking_volume");
1166                                 break;
1167                         case MENU_ACTION_INCREASE_TALKING:
1168                                 ast_cli(a->fd, "increase_talking_volume");
1169                                 break;
1170                         case MENU_ACTION_DECREASE_TALKING:
1171                                 ast_cli(a->fd, "decrease_talking_volume");
1172                                 break;
1173                         case MENU_ACTION_PLAYBACK:
1174                                 ast_cli(a->fd, "playback(%s)", menu_action->data.playback_file);
1175                                 break;
1176                         case MENU_ACTION_PLAYBACK_AND_CONTINUE:
1177                                 ast_cli(a->fd, "playback_and_continue(%s)", menu_action->data.playback_file);
1178                                 break;
1179                         case MENU_ACTION_DIALPLAN_EXEC:
1180                                 ast_cli(a->fd, "dialplan_exec(%s,%s,%d)",
1181                                         menu_action->data.dialplan_args.context,
1182                                         menu_action->data.dialplan_args.exten,
1183                                         menu_action->data.dialplan_args.priority);
1184                                 break;
1185                         case MENU_ACTION_ADMIN_TOGGLE_LOCK:
1186                                 ast_cli(a->fd, "admin_toggle_conference_lock");
1187                                 break;
1188                         case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
1189                                 ast_cli(a->fd, "admin_toggle_mute_participants");
1190                                 break;
1191                         case MENU_ACTION_PARTICIPANT_COUNT:
1192                                 ast_cli(a->fd, "participant_count");
1193                                 break;
1194                         case MENU_ACTION_ADMIN_KICK_LAST:
1195                                 ast_cli(a->fd, "admin_kick_last");
1196                                 break;
1197                         case MENU_ACTION_LEAVE:
1198                                 ast_cli(a->fd, "leave_conference");
1199                                 break;
1200                         case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
1201                                 ast_cli(a->fd, "set_as_single_video_src");
1202                                 break;
1203                         case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
1204                                 ast_cli(a->fd, "release_as_single_video_src");
1205                                 break;
1206                         }
1207                         action_num++;
1208                 }
1209                 ast_cli(a->fd,"\n");
1210         }
1211
1212
1213         ao2_unlock(menu);
1214         ao2_ref(menu, -1);
1215         return CLI_SUCCESS;
1216 }
1217
1218 static struct ast_cli_entry cli_confbridge_parser[] = {
1219         AST_CLI_DEFINE(handle_cli_confbridge_show_user_profile, "Show a conference user profile."),
1220         AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profile, "Show a conference bridge profile."),
1221         AST_CLI_DEFINE(handle_cli_confbridge_show_menu, "Show a conference menu"),
1222         AST_CLI_DEFINE(handle_cli_confbridge_show_user_profiles, "Show a list of conference user profiles."),
1223         AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profiles, "Show a list of conference bridge profiles."),
1224         AST_CLI_DEFINE(handle_cli_confbridge_show_menus, "Show a list of conference menus"),
1225
1226 };
1227
1228 static int conf_parse_init(void)
1229 {
1230         if (!(user_profiles = ao2_container_alloc(283, user_hash_cb, user_cmp_cb))) {
1231                 conf_destroy_config();
1232                 return -1;
1233         }
1234
1235         if (!(bridge_profiles = ao2_container_alloc(283, bridge_hash_cb, bridge_cmp_cb))) {
1236                 conf_destroy_config();
1237                 return -1;
1238         }
1239
1240         if (!(menus = ao2_container_alloc(283, menu_hash_cb, menu_cmp_cb))) {
1241                 conf_destroy_config();
1242                 return -1;
1243         }
1244
1245         ast_cli_register_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser));
1246
1247         return 0;
1248 }
1249
1250 void conf_destroy_config()
1251 {
1252         if (user_profiles) {
1253                 ao2_ref(user_profiles, -1);
1254                 user_profiles = NULL;
1255         }
1256         if (bridge_profiles) {
1257                 ao2_ref(bridge_profiles, -1);
1258                 bridge_profiles = NULL;
1259         }
1260
1261         if (menus) {
1262                 ao2_ref(menus, -1);
1263                 menus = NULL;
1264         }
1265         ast_cli_unregister_multiple(cli_confbridge_parser, sizeof(cli_confbridge_parser) / sizeof(struct ast_cli_entry));
1266 }
1267
1268 static void remove_all_delme(void)
1269 {
1270         ao2_callback(user_profiles, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, match_user_delme_cb, NULL);
1271         ao2_callback(bridge_profiles, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, match_bridge_delme_cb, NULL);
1272         ao2_callback(menus, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, match_menu_delme_cb, NULL);
1273 }
1274
1275 static void mark_all_delme(void)
1276 {
1277         ao2_callback(user_profiles, OBJ_NODATA | OBJ_MULTIPLE, user_mark_delme_cb, NULL);
1278         ao2_callback(bridge_profiles, OBJ_NODATA | OBJ_MULTIPLE, bridge_mark_delme_cb, NULL);
1279         ao2_callback(menus, OBJ_NODATA | OBJ_MULTIPLE, menu_mark_delme_cb, NULL);
1280 }
1281
1282 int conf_load_config(int reload)
1283 {
1284         struct ast_flags config_flags = { 0, };
1285         struct ast_config *cfg = ast_config_load(CONFBRIDGE_CONFIG, config_flags);
1286         const char *type = NULL;
1287         char *cat = NULL;
1288
1289         if (!reload) {
1290                 conf_parse_init();
1291         }
1292
1293         if (!cfg || cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
1294                 return 0;
1295         }
1296
1297         mark_all_delme();
1298
1299         while ((cat = ast_category_browse(cfg, cat))) {
1300                 if (!(type = (ast_variable_retrieve(cfg, cat, "type")))) {
1301                         if (strcasecmp(cat, "general")) {
1302                                 ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat);
1303                         }
1304                         continue;
1305                 }
1306                 if (!strcasecmp(type, "bridge")) {
1307                         parse_bridge(cat, cfg);
1308                 } else if (!strcasecmp(type, "user")) {
1309                         parse_user(cat, cfg);
1310                 } else if (!strcasecmp(type, "menu")) {
1311                         parse_menu(cat, cfg);
1312                 } else {
1313                         continue;
1314                 }
1315         }
1316
1317         remove_all_delme();
1318         ast_config_destroy(cfg);
1319
1320         return 0;
1321 }
1322
1323 static void conf_user_profile_copy(struct user_profile *dst, struct user_profile *src)
1324 {
1325         memcpy(dst, src, sizeof(*dst));
1326 }
1327
1328 const struct user_profile *conf_find_user_profile(struct ast_channel *chan, const char *user_profile_name, struct user_profile *result)
1329 {
1330         struct user_profile tmp;
1331         struct user_profile *tmp2;
1332         struct ast_datastore *datastore = NULL;
1333         struct func_confbridge_data *b_data = NULL;
1334         ast_copy_string(tmp.name, user_profile_name, sizeof(tmp.name));
1335
1336         if (chan) {
1337                 ast_channel_lock(chan);
1338                 if ((datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL))) {
1339                         ast_channel_unlock(chan);
1340                         b_data = datastore->data;
1341                         if (b_data->u_usable) {
1342                                 conf_user_profile_copy(result, &b_data->u_profile);
1343                                 return result;
1344                         }
1345                 } else {
1346                         ast_channel_unlock(chan);
1347                 }
1348         }
1349
1350         if (ast_strlen_zero(user_profile_name)) {
1351                 user_profile_name = DEFAULT_USER_PROFILE;
1352         }
1353         if (!(tmp2 = ao2_find(user_profiles, &tmp, OBJ_POINTER))) {
1354                 return NULL;
1355         }
1356         ao2_lock(tmp2);
1357         conf_user_profile_copy(result, tmp2);
1358         ao2_unlock(tmp2);
1359         ao2_ref(tmp2, -1);
1360
1361         return result;
1362 }
1363
1364 void conf_bridge_profile_copy(struct bridge_profile *dst, struct bridge_profile *src)
1365 {
1366         memcpy(dst, src, sizeof(*dst));
1367         if (src->sounds) {
1368                 ao2_ref(src->sounds, +1);
1369         }
1370 }
1371
1372 void conf_bridge_profile_destroy(struct bridge_profile *b_profile)
1373 {
1374         if (b_profile->sounds) {
1375                 ao2_ref(b_profile->sounds, -1);
1376                 b_profile->sounds = NULL;
1377         }
1378 }
1379
1380 const struct bridge_profile *conf_find_bridge_profile(struct ast_channel *chan, const char *bridge_profile_name, struct bridge_profile *result)
1381 {
1382         struct bridge_profile tmp;
1383         struct bridge_profile *tmp2;
1384         struct ast_datastore *datastore = NULL;
1385         struct func_confbridge_data *b_data = NULL;
1386
1387         if (chan) {
1388                 ast_channel_lock(chan);
1389                 if ((datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL))) {
1390                         ast_channel_unlock(chan);
1391                         b_data = datastore->data;
1392                         if (b_data->b_usable) {
1393                                 conf_bridge_profile_copy(result, &b_data->b_profile);
1394                                 return result;
1395                         }
1396                 } else {
1397                         ast_channel_unlock(chan);
1398                 }
1399         }
1400         if (ast_strlen_zero(bridge_profile_name)) {
1401                 bridge_profile_name = DEFAULT_BRIDGE_PROFILE;
1402         }
1403         ast_copy_string(tmp.name, bridge_profile_name, sizeof(tmp.name));
1404         if (!(tmp2 = ao2_find(bridge_profiles, &tmp, OBJ_POINTER))) {
1405                 return NULL;
1406         }
1407         ao2_lock(tmp2);
1408         conf_bridge_profile_copy(result, tmp2);
1409         ao2_unlock(tmp2);
1410         ao2_ref(tmp2, -1);
1411
1412         return result;
1413 }
1414
1415 struct dtmf_menu_hook_pvt {
1416         struct conference_bridge_user *conference_bridge_user;
1417         struct conf_menu_entry menu_entry;
1418         struct conf_menu *menu;
1419 };
1420
1421 static void menu_hook_destroy(void *hook_pvt)
1422 {
1423         struct dtmf_menu_hook_pvt *pvt = hook_pvt;
1424         struct conf_menu_action *action = NULL;
1425
1426         ao2_ref(pvt->menu, -1);
1427
1428         while ((action = AST_LIST_REMOVE_HEAD(&pvt->menu_entry.actions, action))) {
1429                 ast_free(action);
1430         }
1431         ast_free(pvt);
1432 }
1433
1434 static int menu_hook_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1435 {
1436         struct dtmf_menu_hook_pvt *pvt = hook_pvt;
1437         return conf_handle_dtmf(bridge_channel, pvt->conference_bridge_user, &pvt->menu_entry, pvt->menu);
1438 }
1439
1440 static int copy_menu_entry(struct conf_menu_entry *dst, struct conf_menu_entry *src)
1441 {
1442         struct conf_menu_action *menu_action = NULL;
1443         struct conf_menu_action *new_menu_action = NULL;
1444
1445         memcpy(dst, src, sizeof(*dst));
1446         AST_LIST_HEAD_INIT_NOLOCK(&dst->actions);
1447
1448         AST_LIST_TRAVERSE(&src->actions, menu_action, action) {
1449                 if (!(new_menu_action = ast_calloc(1, sizeof(*new_menu_action)))) {
1450                         return -1;
1451                 }
1452                 memcpy(new_menu_action, menu_action, sizeof(*new_menu_action));
1453                 AST_LIST_INSERT_HEAD(&dst->actions, new_menu_action, action);
1454         }
1455         return 0;
1456 }
1457
1458 void conf_menu_entry_destroy(struct conf_menu_entry *menu_entry)
1459 {
1460         struct conf_menu_action *menu_action = NULL;
1461         while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
1462                 ast_free(menu_action);
1463         }
1464 }
1465
1466 int conf_find_menu_entry_by_sequence(const char *dtmf_sequence, struct conf_menu *menu, struct conf_menu_entry *result)
1467 {
1468         struct conf_menu_entry *menu_entry = NULL;
1469
1470         ao2_lock(menu);
1471         AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
1472                 if (!strcasecmp(menu_entry->dtmf, dtmf_sequence)) {
1473                         copy_menu_entry(result, menu_entry);
1474                         ao2_unlock(menu);
1475                         return 1;
1476                 }
1477         }
1478         ao2_unlock(menu);
1479
1480         return 0;
1481 }
1482
1483 int conf_set_menu_to_user(const char *menu_name, struct conference_bridge_user *conference_bridge_user)
1484 {
1485         struct conf_menu tmp;
1486         struct conf_menu *menu;
1487         struct conf_menu_entry *menu_entry = NULL;
1488         ast_copy_string(tmp.name, menu_name, sizeof(tmp.name));
1489
1490         if (!(menu = ao2_find(menus, &tmp, OBJ_POINTER))) {
1491                 return -1;
1492         }
1493         ao2_lock(menu);
1494         AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
1495                 struct dtmf_menu_hook_pvt *pvt;
1496                 if (!(pvt = ast_calloc(1, sizeof(*pvt)))) {
1497                         ao2_unlock(menu);
1498                         ao2_ref(menu, -1);
1499                         return -1;
1500                 }
1501                 if (copy_menu_entry(&pvt->menu_entry, menu_entry)) {
1502                         ast_free(pvt);
1503                         ao2_unlock(menu);
1504                         ao2_ref(menu, -1);
1505                         return -1;
1506                 }
1507                 pvt->conference_bridge_user = conference_bridge_user;
1508                 ao2_ref(menu, +1);
1509                 pvt->menu = menu;
1510
1511                 ast_bridge_features_hook(&conference_bridge_user->features, pvt->menu_entry.dtmf, menu_hook_callback, pvt, menu_hook_destroy);
1512         }
1513
1514         ao2_unlock(menu);
1515         ao2_ref(menu, -1);
1516
1517         return 0;
1518 }