8864f52bce79a9f0decf0869fe8fdedf8268be11
[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, "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) {
194                         return -1;
195                 }
196         } else if (!strcasecmp(name, "dsp_silence_threshold")) {
197                 if (sscanf(value, "%30u", &u_profile->silence_threshold) != 1) {
198                         return -1;
199                 }
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))) {
204                         return -1;
205                 }
206         } else if (!strcasecmp(name, "jitterbuffer")) {
207                 ast_set2_flag(u_profile, ast_true(value), USER_OPT_JITTERBUFFER);
208         } else {
209                 return -1;
210         }
211         return 0;
212 }
213
214 static int set_sound(const char *sound_name, const char *sound_file, struct bridge_profile_sounds *sounds)
215 {
216         if (ast_strlen_zero(sound_file)) {
217                 return -1;
218         }
219
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);
256         } else {
257                 return -1;
258         }
259
260         return 0;
261 }
262 static int set_bridge_option(const char *name, const char *value, struct bridge_profile *b_profile)
263 {
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) {
268                         return -1;
269                 }
270         } else if (!strcasecmp(name, "mixing_interval")) {
271                 if (sscanf(value, "%30u", &b_profile->mix_interval) != 1) {
272                         return -1;
273                 }
274                 switch (b_profile->mix_interval) {
275                 case 10:
276                 case 20:
277                 case 40:
278                 case 80:
279                         break;
280                 default:
281                         ast_log(LOG_WARNING, "invalid mixing interval %u\n", b_profile->mix_interval);
282                         b_profile->mix_interval = 0;
283                         return -1;
284                 }
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);
294                 }
295         } else if (!strcasecmp(name, "max_members")) {
296                 if (sscanf(value, "%30u", &b_profile->max_members) != 1) {
297                         return -1;
298                 }
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)) {
303                         return -1;
304                 }
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;
309                 if (!sounds) {
310                         return -1;
311                 }
312                 if (!(conf_find_bridge_profile(NULL, value, tmp))) {
313                         ao2_ref(sounds, -1);
314                         return -1;
315                 }
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);
335
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. */
339         } else {
340                 return -1;
341         }
342
343         return 0;
344 }
345
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 */
352 };
353 static void func_confbridge_destroy_cb(void *data)
354 {
355         struct func_confbridge_data *b_data = data;
356         conf_bridge_profile_destroy(&b_data->b_profile);
357         ast_free(b_data);
358 };
359 static const struct ast_datastore_info confbridge_datastore = {
360         .type = "confbridge",
361         .destroy = func_confbridge_destroy_cb
362 };
363 int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value)
364 {
365         struct ast_datastore *datastore = NULL;
366         struct func_confbridge_data *b_data = NULL;
367         char *parse = NULL;
368         int new = 0;
369         AST_DECLARE_APP_ARGS(args,
370                 AST_APP_ARG(type);
371                 AST_APP_ARG(option);
372         );
373
374         /* parse all the required arguments and make sure they exist. */
375         if (ast_strlen_zero(data) || ast_strlen_zero(value)) {
376                 return -1;
377         }
378         parse = ast_strdupa(data);
379         AST_STANDARD_APP_ARGS(args, parse);
380         if (ast_strlen_zero(args.type) || ast_strlen_zero(args.option)) {
381                 return -1;
382         }
383
384         ast_channel_lock(chan);
385         if (!(datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL))) {
386                 ast_channel_unlock(chan);
387
388                 if (!(datastore = ast_datastore_alloc(&confbridge_datastore, NULL))) {
389                         return 0;
390                 }
391                 if (!(b_data = ast_calloc(1, sizeof(*b_data)))) {
392                         ast_datastore_free(datastore);
393                         return 0;
394                 }
395                 if (!(b_data->b_profile.sounds = bridge_profile_sounds_alloc())) {
396                         ast_datastore_free(datastore);
397                         ast_free(b_data);
398                         return 0;
399                 }
400                 datastore->data = b_data;
401                 new = 1;
402         } else {
403                 ast_channel_unlock(chan);
404                 b_data = datastore->data;
405         }
406
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;
412         } else {
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);
415                 goto cleanup_error;
416         }
417         if (new) {
418                 ast_channel_lock(chan);
419                 ast_channel_datastore_add(chan, datastore);
420                 ast_channel_unlock(chan);
421         }
422         return 0;
423
424 cleanup_error:
425         ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
426         if (new) {
427                 ast_datastore_free(datastore);
428         }
429         return -1;
430 }
431
432 /*!
433  * \brief Parse the bridge profile options
434  */
435 static int parse_bridge(const char *cat, struct ast_config *cfg)
436 {
437         struct ast_variable *var;
438         struct bridge_profile tmp;
439         struct bridge_profile *b_profile;
440
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);
447         } else {
448                 return -1;
449         }
450
451         ao2_lock(b_profile);
452         /* set defaults */
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;
463         }
464
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);
469                 return -1;
470         }
471
472         for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
473                 if (!strcasecmp(var->name, "type")) {
474                         continue;
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);
478                 }
479         }
480         ao2_unlock(b_profile);
481
482         ao2_ref(b_profile, -1);
483         return 0;
484 }
485
486 static int parse_user(const char *cat, struct ast_config *cfg)
487 {
488         struct ast_variable *var;
489         struct user_profile tmp;
490         struct user_profile *u_profile;
491
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);
498         } else {
499                 return -1;
500         }
501
502         ao2_lock(u_profile);
503         /* set defaults */
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")) {
512                         continue;
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);
516                 }
517         }
518         ao2_unlock(u_profile);
519
520         ao2_ref(u_profile, -1);
521         return 0;
522 }
523
524 static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum conf_menu_action_id id, char *databuf)
525 {
526         struct conf_menu_action *menu_action = ast_calloc(1, sizeof(*menu_action));
527
528         if (!menu_action) {
529                 return -1;
530         }
531         menu_action->id = id;
532
533         switch (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                 break;
547         case MENU_ACTION_PLAYBACK:
548         case MENU_ACTION_PLAYBACK_AND_CONTINUE:
549                 if (!(ast_strlen_zero(databuf))) {
550                         ast_copy_string(menu_action->data.playback_file, databuf, sizeof(menu_action->data.playback_file));
551                 } else {
552                         ast_free(menu_action);
553                         return -1;
554                 }
555                 break;
556         case MENU_ACTION_DIALPLAN_EXEC:
557                 if (!(ast_strlen_zero(databuf))) {
558                         AST_DECLARE_APP_ARGS(args,
559                                 AST_APP_ARG(context);
560                                 AST_APP_ARG(exten);
561                                 AST_APP_ARG(priority);
562                         );
563                         AST_STANDARD_APP_ARGS(args, databuf);
564                         if (!ast_strlen_zero(args.context)) {
565                                 ast_copy_string(menu_action->data.dialplan_args.context,
566                                         args.context,
567                                         sizeof(menu_action->data.dialplan_args.context));
568                         }
569                         if (!ast_strlen_zero(args.exten)) {
570                                 ast_copy_string(menu_action->data.dialplan_args.exten,
571                                         args.exten,
572                                         sizeof(menu_action->data.dialplan_args.exten));
573                         }
574                         menu_action->data.dialplan_args.priority = 1; /* 1 by default */
575                         if (!ast_strlen_zero(args.priority) &&
576                                 (sscanf(args.priority, "%30u", &menu_action->data.dialplan_args.priority) != 1)) {
577                                 /* invalid priority */
578                                 ast_free(menu_action);
579                                 return -1;
580                         }
581                 } else {
582                         ast_free(menu_action);
583                         return -1;
584                 }
585         };
586
587         AST_LIST_INSERT_TAIL(&menu_entry->actions, menu_action, action);
588
589         return 0;
590 }
591
592 static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *action_names)
593 {
594         struct conf_menu_entry *menu_entry = NULL, *cur = NULL;
595         int res = 0;
596         char *tmp_action_names = ast_strdupa(action_names);
597         char *action = NULL;
598         char *action_args;
599         char *tmp;
600         char buf[PATH_MAX];
601         char *delimiter = ",";
602
603         if (!(menu_entry = ast_calloc(1, sizeof(*menu_entry)))) {
604                 return -1;
605         }
606
607         for (;;) {
608                 char *comma;
609                 char *startbrace;
610                 char *endbrace;
611                 unsigned int action_len;
612
613                 if (ast_strlen_zero(tmp_action_names)) {
614                         break;
615                 }
616                 startbrace = strchr(tmp_action_names, '(');
617                 endbrace = strchr(tmp_action_names, ')');
618                 comma = strchr(tmp_action_names, ',');
619
620                 /* If the next action has brackets with comma delimited arguments in it,
621                  * make the delimeter ')' instead of a comma to preserve the argments */
622                 if (startbrace && endbrace && comma && (comma > startbrace && comma < endbrace)) {
623                         delimiter = ")";
624                 } else {
625                         delimiter = ",";
626                 }
627
628                 if (!(action = strsep(&tmp_action_names, delimiter))) {
629                         break;
630                 }
631
632                 action = ast_strip(action);
633                 if (ast_strlen_zero(action)) {
634                         continue;
635                 }
636
637                 action_len = strlen(action);
638                 ast_copy_string(menu_entry->dtmf, dtmf, sizeof(menu_entry->dtmf));
639                 if (!strcasecmp(action, "toggle_mute")) {
640                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_TOGGLE_MUTE, NULL);
641                 } else if (!strcasecmp(action, "no_op")) {
642                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_NOOP, NULL);
643                 } else if (!strcasecmp(action, "increase_listening_volume")) {
644                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_LISTENING, NULL);
645                 } else if (!strcasecmp(action, "decrease_listening_volume")) {
646                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_LISTENING, NULL);
647                 } else if (!strcasecmp(action, "increase_talking_volume")) {
648                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_TALKING, NULL);
649                 } else if (!strcasecmp(action, "reset_listening_volume")) {
650                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_LISTENING, NULL);
651                 } else if (!strcasecmp(action, "reset_talking_volume")) {
652                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_TALKING, NULL);
653                 } else if (!strcasecmp(action, "decrease_talking_volume")) {
654                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_TALKING, NULL);
655                 } else if (!strcasecmp(action, "admin_toggle_conference_lock")) {
656                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_LOCK, NULL);
657                 } else if (!strcasecmp(action, "admin_kick_last")) {
658                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_KICK_LAST, NULL);
659                 } else if (!strcasecmp(action, "leave_conference")) {
660                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_LEAVE, NULL);
661                 } else if (!strcasecmp(action, "set_as_single_video_src")) {
662                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_SET_SINGLE_VIDEO_SRC, NULL);
663                 } else if (!strncasecmp(action, "dialplan_exec(", 14)) {
664                         ast_copy_string(buf, action, sizeof(buf));
665                         action_args = buf;
666                         if ((action_args = strchr(action, '('))) {
667                                 action_args++;
668                         }
669                         /* it is possible that this argument may or may not
670                          * have a closing brace at this point, it all depends on if
671                          * comma delimited arguments were provided */
672                         if ((tmp = strchr(action, ')'))) {
673                                 *tmp = '\0';
674                         }
675                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DIALPLAN_EXEC, action_args);
676                 } else if (action_len >= 21 && !strncasecmp(action, "playback_and_continue(", 22)) {
677                         ast_copy_string(buf, action, sizeof(buf));
678                         action_args = buf;
679                         if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
680                                 *tmp = '\0';
681                                 action_args++;
682                         }
683                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK_AND_CONTINUE, action_args);
684                 } else if (action_len >= 8 && !strncasecmp(action, "playback(", 9)) {
685                         ast_copy_string(buf, action, sizeof(buf));
686                         action_args = buf;
687                         if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
688                                 *tmp = '\0';
689                                 action_args++;
690                         }
691                         res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK, action_args);
692                 }
693         }
694
695         /* if adding any of the actions failed, bail */
696         if (res) {
697                 struct conf_menu_action *action;
698                 while ((action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
699                         ast_free(action);
700                 }
701                 ast_free(menu_entry);
702                 return -1;
703         }
704
705         /* remove any list entry with an identical DTMF sequence for overrides */
706         AST_LIST_TRAVERSE_SAFE_BEGIN(&menu->entries, cur, entry) {
707                 if (!strcasecmp(cur->dtmf, menu_entry->dtmf)) {
708                         AST_LIST_REMOVE_CURRENT(entry);
709                         ast_free(cur);
710                         break;
711                 }
712         }
713         AST_LIST_TRAVERSE_SAFE_END;
714
715         AST_LIST_INSERT_TAIL(&menu->entries, menu_entry, entry);
716
717         return 0;
718 }
719 static int parse_menu(const char *cat, struct ast_config *cfg)
720 {
721         struct ast_variable *var;
722         struct conf_menu tmp;
723         struct conf_menu *menu;
724
725         ast_copy_string(tmp.name, cat, sizeof(tmp.name));
726         if ((menu = ao2_find(menus, &tmp, OBJ_POINTER))) {
727                 menu->delme = 0;
728         } else if ((menu = ao2_alloc(sizeof(*menu), menu_destructor))) {
729                 ast_copy_string(menu->name, cat, sizeof(menu->name));
730                 ao2_link(menus, menu);
731         } else {
732                 return -1;
733         }
734
735         ao2_lock(menu);
736         /* this isn't freeing the menu, just destroying the menu list so it can be rebuilt.*/
737         menu_destructor(menu);
738         for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
739                 if (!strcasecmp(var->name, "type")) {
740                         continue;
741                 } else if (add_menu_entry(menu, var->name, var->value)) {
742                         ast_log(LOG_WARNING, "Unknown option '%s' at line %d of %s is not supported.\n",
743                                 var->name, var->lineno, CONFBRIDGE_CONFIG);
744                 }
745         }
746         ao2_unlock(menu);
747
748         ao2_ref(menu, -1);
749         return 0;
750 }
751
752 static char *complete_user_profile_name(const char *line, const char *word, int pos, int state)
753 {
754         int which = 0;
755         char *res = NULL;
756         int wordlen = strlen(word);
757         struct ao2_iterator i;
758         struct user_profile *u_profile = NULL;
759
760         i = ao2_iterator_init(user_profiles, 0);
761         while ((u_profile = ao2_iterator_next(&i))) {
762                 if (!strncasecmp(u_profile->name, word, wordlen) && ++which > state) {
763                         res = ast_strdup(u_profile->name);
764                         ao2_ref(u_profile, -1);
765                         break;
766                 }
767                 ao2_ref(u_profile, -1);
768         }
769         ao2_iterator_destroy(&i);
770
771         return res;
772 }
773
774 static char *handle_cli_confbridge_show_user_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
775 {
776         struct ao2_iterator it;
777         struct user_profile *u_profile;
778
779         switch (cmd) {
780         case CLI_INIT:
781                 e->command = "confbridge show profile users";
782                 e->usage =
783                         "Usage confbridge show profile users\n";
784                 return NULL;
785         case CLI_GENERATE:
786                 return NULL;
787         }
788
789         ast_cli(a->fd,"--------- User Profiles -----------\n");
790         ao2_lock(user_profiles);
791         it = ao2_iterator_init(user_profiles, 0);
792         while ((u_profile = ao2_iterator_next(&it))) {
793                 ast_cli(a->fd,"%s\n", u_profile->name);
794                 ao2_ref(u_profile, -1);
795         }
796         ao2_iterator_destroy(&it);
797         ao2_unlock(user_profiles);
798
799         return CLI_SUCCESS;
800 }
801 static char *handle_cli_confbridge_show_user_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
802 {
803         struct user_profile u_profile;
804
805         switch (cmd) {
806         case CLI_INIT:
807                 e->command = "confbridge show profile user";
808                 e->usage =
809                         "Usage confbridge show profile user [<profile name>]\n";
810                 return NULL;
811         case CLI_GENERATE:
812                 if (a->pos == 4) {
813                         return complete_user_profile_name(a->line, a->word, a->pos, a->n);
814                 }
815                 return NULL;
816         }
817
818         if (a->argc != 5) {
819                 return CLI_SHOWUSAGE;
820         }
821
822         if (!(conf_find_user_profile(NULL, a->argv[4], &u_profile))) {
823                 ast_cli(a->fd, "No conference user profile named '%s' found!\n", a->argv[4]);
824                 return CLI_SUCCESS;
825         }
826
827         ast_cli(a->fd,"--------------------------------------------\n");
828         ast_cli(a->fd,"Name:                    %s\n",
829                 u_profile.name);
830         ast_cli(a->fd,"Admin:                   %s\n",
831                 u_profile.flags & USER_OPT_ADMIN ?
832                 "true" : "false");
833         ast_cli(a->fd,"Marked User:             %s\n",
834                 u_profile.flags & USER_OPT_MARKEDUSER ?
835                 "true" : "false");
836         ast_cli(a->fd,"Start Muted:             %s\n",
837                 u_profile.flags & USER_OPT_STARTMUTED?
838                 "true" : "false");
839         ast_cli(a->fd,"MOH When Empty:          %s\n",
840                 u_profile.flags & USER_OPT_MUSICONHOLD ?
841                 "enabled" : "disabled");
842         ast_cli(a->fd,"MOH Class:               %s\n",
843                 ast_strlen_zero(u_profile.moh_class) ?
844                 "default" : u_profile.moh_class);
845         ast_cli(a->fd,"Quiet:                   %s\n",
846                 u_profile.flags & USER_OPT_QUIET ?
847                 "enabled" : "disabled");
848         ast_cli(a->fd,"Wait Marked:             %s\n",
849                 u_profile.flags & USER_OPT_WAITMARKED ?
850                 "enabled" : "disabled");
851         ast_cli(a->fd,"END Marked:              %s\n",
852                 u_profile.flags & USER_OPT_ENDMARKED ?
853                 "enabled" : "disabled");
854         ast_cli(a->fd,"Drop_silence:            %s\n",
855                 u_profile.flags & USER_OPT_DROP_SILENCE ?
856                 "enabled" : "disabled");
857         ast_cli(a->fd,"Silence Threshold:       %dms\n",
858                 u_profile.silence_threshold);
859         ast_cli(a->fd,"Talking Threshold:       %dms\n",
860                 u_profile.talking_threshold);
861         ast_cli(a->fd,"Denoise:                 %s\n",
862                 u_profile.flags & USER_OPT_DENOISE ?
863                 "enabled" : "disabled");
864         ast_cli(a->fd,"Jitterbuffer:            %s\n",
865                 u_profile.flags & USER_OPT_JITTERBUFFER ?
866                 "enabled" : "disabled");
867         ast_cli(a->fd,"Talk Detect Events:      %s\n",
868                 u_profile.flags & USER_OPT_TALKER_DETECT ?
869                 "enabled" : "disabled");
870         ast_cli(a->fd,"DTMF Pass Through:       %s\n",
871                 u_profile.flags & USER_OPT_DTMF_PASS ?
872                 "enabled" : "disabled");
873         ast_cli(a->fd,"PIN:                     %s\n",
874                 ast_strlen_zero(u_profile.pin) ?
875                 "None" : u_profile.pin);
876         ast_cli(a->fd,"Announce User Count:     %s\n",
877                 u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNT ?
878                 "enabled" : "disabled");
879         ast_cli(a->fd,"Announce join/leave:     %s\n",
880                 u_profile.flags & USER_OPT_ANNOUNCE_JOIN_LEAVE ?
881                 "enabled" : "disabled");
882         ast_cli(a->fd,"Announce User Count all: %s\n",
883                 u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNTALL ?
884                 "enabled" : "disabled");
885                 ast_cli(a->fd,"\n");
886
887         return CLI_SUCCESS;
888 }
889
890 static char *complete_bridge_profile_name(const char *line, const char *word, int pos, int state)
891 {
892         int which = 0;
893         char *res = NULL;
894         int wordlen = strlen(word);
895         struct ao2_iterator i;
896         struct bridge_profile *b_profile = NULL;
897
898         i = ao2_iterator_init(bridge_profiles, 0);
899         while ((b_profile = ao2_iterator_next(&i))) {
900                 if (!strncasecmp(b_profile->name, word, wordlen) && ++which > state) {
901                         res = ast_strdup(b_profile->name);
902                         ao2_ref(b_profile, -1);
903                         break;
904                 }
905                 ao2_ref(b_profile, -1);
906         }
907         ao2_iterator_destroy(&i);
908
909         return res;
910 }
911
912 static char *handle_cli_confbridge_show_bridge_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
913 {
914         struct ao2_iterator it;
915         struct bridge_profile *b_profile;
916
917         switch (cmd) {
918         case CLI_INIT:
919                 e->command = "confbridge show profile bridges";
920                 e->usage =
921                         "Usage confbridge show profile bridges\n";
922                 return NULL;
923         case CLI_GENERATE:
924                 return NULL;
925         }
926
927         ast_cli(a->fd,"--------- Bridge Profiles -----------\n");
928         ao2_lock(bridge_profiles);
929         it = ao2_iterator_init(bridge_profiles, 0);
930         while ((b_profile = ao2_iterator_next(&it))) {
931                 ast_cli(a->fd,"%s\n", b_profile->name);
932                 ao2_ref(b_profile, -1);
933         }
934         ao2_iterator_destroy(&it);
935         ao2_unlock(bridge_profiles);
936
937         return CLI_SUCCESS;
938 }
939
940 static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
941 {
942         struct bridge_profile b_profile;
943         char tmp[64];
944
945         switch (cmd) {
946         case CLI_INIT:
947                 e->command = "confbridge show profile bridge";
948                 e->usage =
949                         "Usage confbridge show profile bridge <profile name>\n";
950                 return NULL;
951         case CLI_GENERATE:
952                 if (a->pos == 4) {
953                         return complete_bridge_profile_name(a->line, a->word, a->pos, a->n);
954                 }
955                 return NULL;
956         }
957
958         if (a->argc != 5) {
959                 return CLI_SHOWUSAGE;
960         }
961
962         if (!(conf_find_bridge_profile(NULL, a->argv[4], &b_profile))) {
963                 ast_cli(a->fd, "No conference bridge profile named '%s' found!\n", a->argv[4]);
964                 return CLI_SUCCESS;
965         }
966
967         ast_cli(a->fd,"--------------------------------------------\n");
968         ast_cli(a->fd,"Name:                 %s\n", b_profile.name);
969
970         if (b_profile.internal_sample_rate) {
971                 snprintf(tmp, sizeof(tmp), "%d", b_profile.internal_sample_rate);
972         } else {
973                 ast_copy_string(tmp, "auto", sizeof(tmp));
974         }
975         ast_cli(a->fd,"Internal Sample Rate: %s\n", tmp);
976
977         if (b_profile.mix_interval) {
978                 ast_cli(a->fd,"Mixing Interval:      %d\n", b_profile.mix_interval);
979         } else {
980                 ast_cli(a->fd,"Mixing Interval:      Default 20ms\n");
981         }
982
983         ast_cli(a->fd,"Record Conference:    %s\n",
984                 b_profile.flags & BRIDGE_OPT_RECORD_CONFERENCE ?
985                 "yes" : "no");
986
987         ast_cli(a->fd,"Record File:          %s\n",
988                 ast_strlen_zero(b_profile.rec_file) ? "Auto Generated" :
989                 b_profile.rec_file);
990
991         if (b_profile.max_members) {
992                 ast_cli(a->fd,"Max Members:          %d\n", b_profile.max_members);
993         } else {
994                 ast_cli(a->fd,"Max Members:          No Limit\n");
995         }
996
997         if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_LAST_MARKED) {
998                 ast_cli(a->fd, "Video Mode:           last_marked\n");
999         } else if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) {
1000                 ast_cli(a->fd, "Video Mode:           first_marked\n");
1001         } else if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER) {
1002                 ast_cli(a->fd, "Video Mode:           follow_talker\n");
1003         } else {
1004                 ast_cli(a->fd, "Video Mode:           no video\n");
1005         }
1006
1007         ast_cli(a->fd,"sound_join:           %s\n", conf_get_sound(CONF_SOUND_JOIN, b_profile.sounds));
1008         ast_cli(a->fd,"sound_leave:          %s\n", conf_get_sound(CONF_SOUND_LEAVE, b_profile.sounds));
1009         ast_cli(a->fd,"sound_only_person:    %s\n", conf_get_sound(CONF_SOUND_ONLY_PERSON, b_profile.sounds));
1010         ast_cli(a->fd,"sound_has_joined:     %s\n", conf_get_sound(CONF_SOUND_HAS_JOINED, b_profile.sounds));
1011         ast_cli(a->fd,"sound_has_left:       %s\n", conf_get_sound(CONF_SOUND_HAS_LEFT, b_profile.sounds));
1012         ast_cli(a->fd,"sound_kicked:         %s\n", conf_get_sound(CONF_SOUND_KICKED, b_profile.sounds));
1013         ast_cli(a->fd,"sound_muted:          %s\n", conf_get_sound(CONF_SOUND_MUTED, b_profile.sounds));
1014         ast_cli(a->fd,"sound_unmuted:        %s\n", conf_get_sound(CONF_SOUND_UNMUTED, b_profile.sounds));
1015         ast_cli(a->fd,"sound_there_are:      %s\n", conf_get_sound(CONF_SOUND_THERE_ARE, b_profile.sounds));
1016         ast_cli(a->fd,"sound_other_in_party: %s\n", conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, b_profile.sounds));
1017         ast_cli(a->fd,"sound_place_into_conference: %s\n", conf_get_sound(CONF_SOUND_PLACE_IN_CONF, b_profile.sounds));
1018         ast_cli(a->fd,"sound_wait_for_leader:       %s\n", conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, b_profile.sounds));
1019         ast_cli(a->fd,"sound_get_pin:        %s\n", conf_get_sound(CONF_SOUND_GET_PIN, b_profile.sounds));
1020         ast_cli(a->fd,"sound_invalid_pin:    %s\n", conf_get_sound(CONF_SOUND_INVALID_PIN, b_profile.sounds));
1021         ast_cli(a->fd,"sound_locked:         %s\n", conf_get_sound(CONF_SOUND_LOCKED, b_profile.sounds));
1022         ast_cli(a->fd,"sound_unlocked_now:   %s\n", conf_get_sound(CONF_SOUND_UNLOCKED_NOW, b_profile.sounds));
1023         ast_cli(a->fd,"sound_lockednow:      %s\n", conf_get_sound(CONF_SOUND_LOCKED_NOW, b_profile.sounds));
1024         ast_cli(a->fd,"sound_error_menu:     %s\n", conf_get_sound(CONF_SOUND_ERROR_MENU, b_profile.sounds));
1025         ast_cli(a->fd,"\n");
1026
1027         conf_bridge_profile_destroy(&b_profile);
1028         return CLI_SUCCESS;
1029 }
1030
1031 static char *complete_menu_name(const char *line, const char *word, int pos, int state)
1032 {
1033         int which = 0;
1034         char *res = NULL;
1035         int wordlen = strlen(word);
1036         struct ao2_iterator i;
1037         struct conf_menu *menu = NULL;
1038
1039         i = ao2_iterator_init(menus, 0);
1040         while ((menu = ao2_iterator_next(&i))) {
1041                 if (!strncasecmp(menu->name, word, wordlen) && ++which > state) {
1042                         res = ast_strdup(menu->name);
1043                         ao2_ref(menu, -1);
1044                         break;
1045                 }
1046                 ao2_ref(menu, -1);
1047         }
1048         ao2_iterator_destroy(&i);
1049
1050         return res;
1051 }
1052
1053 static char *handle_cli_confbridge_show_menus(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1054 {
1055         struct ao2_iterator it;
1056         struct conf_menu *menu;
1057
1058         switch (cmd) {
1059         case CLI_INIT:
1060                 e->command = "confbridge show menus";
1061                 e->usage =
1062                         "Usage confbridge show profile menus\n";
1063                 return NULL;
1064         case CLI_GENERATE:
1065                 return NULL;
1066         }
1067
1068         ast_cli(a->fd,"--------- Menus -----------\n");
1069         ao2_lock(menus);
1070         it = ao2_iterator_init(menus, 0);
1071         while ((menu = ao2_iterator_next(&it))) {
1072                 ast_cli(a->fd,"%s\n", menu->name);
1073                 ao2_ref(menu, -1);
1074         }
1075         ao2_iterator_destroy(&it);
1076         ao2_unlock(menus);
1077
1078         return CLI_SUCCESS;
1079 }
1080
1081 static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1082 {
1083         struct conf_menu tmp;
1084         struct conf_menu *menu;
1085         struct conf_menu_entry *menu_entry = NULL;
1086         struct conf_menu_action *menu_action = NULL;
1087
1088         switch (cmd) {
1089         case CLI_INIT:
1090                 e->command = "confbridge show menu";
1091                 e->usage =
1092                         "Usage confbridge show menu [<menu name>]\n";
1093                 return NULL;
1094         case CLI_GENERATE:
1095                 if (a->pos == 3) {
1096                         return complete_menu_name(a->line, a->word, a->pos, a->n);
1097                 }
1098                 return NULL;
1099         }
1100
1101         if (a->argc != 4) {
1102                 return CLI_SHOWUSAGE;
1103         }
1104
1105         ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
1106         if (!(menu = ao2_find(menus, &tmp, OBJ_POINTER))) {
1107                 ast_cli(a->fd, "No conference menu named '%s' found!\n", a->argv[3]);
1108                 return CLI_SUCCESS;
1109         }
1110         ao2_lock(menu);
1111
1112         ast_cli(a->fd,"Name: %s\n", menu->name);
1113         AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
1114                 int action_num = 0;
1115                 ast_cli(a->fd, "%s=", menu_entry->dtmf);
1116                 AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
1117                         if (action_num) {
1118                                 ast_cli(a->fd, ", ");
1119                         }
1120                         switch (menu_action->id) {
1121                         case MENU_ACTION_TOGGLE_MUTE:
1122                                 ast_cli(a->fd, "toggle_mute");
1123                                 break;
1124                         case MENU_ACTION_NOOP:
1125                                 ast_cli(a->fd, "no_op");
1126                                 break;
1127                         case MENU_ACTION_INCREASE_LISTENING:
1128                                 ast_cli(a->fd, "increase_listening_volume");
1129                                 break;
1130                         case MENU_ACTION_DECREASE_LISTENING:
1131                                 ast_cli(a->fd, "decrease_listening_volume");
1132                                 break;
1133                         case MENU_ACTION_RESET_LISTENING:
1134                                 ast_cli(a->fd, "reset_listening_volume");
1135                                 break;
1136                         case MENU_ACTION_RESET_TALKING:
1137                                 ast_cli(a->fd, "reset_talking_volume");
1138                                 break;
1139                         case MENU_ACTION_INCREASE_TALKING:
1140                                 ast_cli(a->fd, "increase_talking_volume");
1141                                 break;
1142                         case MENU_ACTION_DECREASE_TALKING:
1143                                 ast_cli(a->fd, "decrease_talking_volume");
1144                                 break;
1145                         case MENU_ACTION_PLAYBACK:
1146                                 ast_cli(a->fd, "playback(%s)", menu_action->data.playback_file);
1147                                 break;
1148                         case MENU_ACTION_PLAYBACK_AND_CONTINUE:
1149                                 ast_cli(a->fd, "playback_and_continue(%s)", menu_action->data.playback_file);
1150                                 break;
1151                         case MENU_ACTION_DIALPLAN_EXEC:
1152                                 ast_cli(a->fd, "dialplan_exec(%s,%s,%d)",
1153                                         menu_action->data.dialplan_args.context,
1154                                         menu_action->data.dialplan_args.exten,
1155                                         menu_action->data.dialplan_args.priority);
1156                                 break;
1157                         case MENU_ACTION_ADMIN_TOGGLE_LOCK:
1158                                 ast_cli(a->fd, "admin_toggle_conference_lock");
1159                                 break;
1160                         case MENU_ACTION_ADMIN_KICK_LAST:
1161                                 ast_cli(a->fd, "admin_kick_last");
1162                                 break;
1163                         case MENU_ACTION_LEAVE:
1164                                 ast_cli(a->fd, "leave_conference");
1165                                 break;
1166                         case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
1167                                 ast_cli(a->fd, "set_as_single_video_src");
1168                                 break;
1169                         }
1170                         action_num++;
1171                 }
1172                 ast_cli(a->fd,"\n");
1173         }
1174
1175
1176         ao2_unlock(menu);
1177         ao2_ref(menu, -1);
1178         return CLI_SUCCESS;
1179 }
1180
1181 static struct ast_cli_entry cli_confbridge_parser[] = {
1182         AST_CLI_DEFINE(handle_cli_confbridge_show_user_profile, "Show a conference user profile."),
1183         AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profile, "Show a conference bridge profile."),
1184         AST_CLI_DEFINE(handle_cli_confbridge_show_menu, "Show a conference menu"),
1185         AST_CLI_DEFINE(handle_cli_confbridge_show_user_profiles, "Show a list of conference user profiles."),
1186         AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profiles, "Show a list of conference bridge profiles."),
1187         AST_CLI_DEFINE(handle_cli_confbridge_show_menus, "Show a list of conference menus"),
1188
1189 };
1190
1191 static int conf_parse_init(void)
1192 {
1193         if (!(user_profiles = ao2_container_alloc(283, user_hash_cb, user_cmp_cb))) {
1194                 conf_destroy_config();
1195                 return -1;
1196         }
1197
1198         if (!(bridge_profiles = ao2_container_alloc(283, bridge_hash_cb, bridge_cmp_cb))) {
1199                 conf_destroy_config();
1200                 return -1;
1201         }
1202
1203         if (!(menus = ao2_container_alloc(283, menu_hash_cb, menu_cmp_cb))) {
1204                 conf_destroy_config();
1205                 return -1;
1206         }
1207
1208         ast_cli_register_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser));
1209
1210         return 0;
1211 }
1212
1213 void conf_destroy_config()
1214 {
1215         if (user_profiles) {
1216                 ao2_ref(user_profiles, -1);
1217                 user_profiles = NULL;
1218         }
1219         if (bridge_profiles) {
1220                 ao2_ref(bridge_profiles, -1);
1221                 bridge_profiles = NULL;
1222         }
1223
1224         if (menus) {
1225                 ao2_ref(menus, -1);
1226                 menus = NULL;
1227         }
1228         ast_cli_unregister_multiple(cli_confbridge_parser, sizeof(cli_confbridge_parser) / sizeof(struct ast_cli_entry));
1229 }
1230
1231 static void remove_all_delme(void)
1232 {
1233         ao2_callback(user_profiles, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, match_user_delme_cb, NULL);
1234         ao2_callback(bridge_profiles, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, match_bridge_delme_cb, NULL);
1235         ao2_callback(menus, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, match_menu_delme_cb, NULL);
1236 }
1237
1238 static void mark_all_delme(void)
1239 {
1240         ao2_callback(user_profiles, OBJ_NODATA | OBJ_MULTIPLE, user_mark_delme_cb, NULL);
1241         ao2_callback(bridge_profiles, OBJ_NODATA | OBJ_MULTIPLE, bridge_mark_delme_cb, NULL);
1242         ao2_callback(menus, OBJ_NODATA | OBJ_MULTIPLE, menu_mark_delme_cb, NULL);
1243 }
1244
1245 int conf_load_config(int reload)
1246 {
1247         struct ast_flags config_flags = { 0, };
1248         struct ast_config *cfg = ast_config_load(CONFBRIDGE_CONFIG, config_flags);
1249         const char *type = NULL;
1250         char *cat = NULL;
1251
1252         if (!reload) {
1253                 conf_parse_init();
1254         }
1255
1256         if (!cfg || cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
1257                 return 0;
1258         }
1259
1260         mark_all_delme();
1261
1262         while ((cat = ast_category_browse(cfg, cat))) {
1263                 if (!(type = (ast_variable_retrieve(cfg, cat, "type")))) {
1264                         if (strcasecmp(cat, "general")) {
1265                                 ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat);
1266                         }
1267                         continue;
1268                 }
1269                 if (!strcasecmp(type, "bridge")) {
1270                         parse_bridge(cat, cfg);
1271                 } else if (!strcasecmp(type, "user")) {
1272                         parse_user(cat, cfg);
1273                 } else if (!strcasecmp(type, "menu")) {
1274                         parse_menu(cat, cfg);
1275                 } else {
1276                         continue;
1277                 }
1278         }
1279
1280         remove_all_delme();
1281
1282         return 0;
1283 }
1284
1285 static void conf_user_profile_copy(struct user_profile *dst, struct user_profile *src)
1286 {
1287         memcpy(dst, src, sizeof(*dst));
1288 }
1289
1290 const struct user_profile *conf_find_user_profile(struct ast_channel *chan, const char *user_profile_name, struct user_profile *result)
1291 {
1292         struct user_profile tmp;
1293         struct user_profile *tmp2;
1294         struct ast_datastore *datastore = NULL;
1295         struct func_confbridge_data *b_data = NULL;
1296         ast_copy_string(tmp.name, user_profile_name, sizeof(tmp.name));
1297
1298         if (chan) {
1299                 ast_channel_lock(chan);
1300                 if ((datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL))) {
1301                         ast_channel_unlock(chan);
1302                         b_data = datastore->data;
1303                         if (b_data->u_usable) {
1304                                 conf_user_profile_copy(result, &b_data->u_profile);
1305                                 return result;
1306                         }
1307                 }
1308                 ast_channel_unlock(chan);
1309         }
1310
1311         if (ast_strlen_zero(user_profile_name)) {
1312                 user_profile_name = DEFAULT_USER_PROFILE;
1313         }
1314         if (!(tmp2 = ao2_find(user_profiles, &tmp, OBJ_POINTER))) {
1315                 return NULL;
1316         }
1317         ao2_lock(tmp2);
1318         conf_user_profile_copy(result, tmp2);
1319         ao2_unlock(tmp2);
1320         ao2_ref(tmp2, -1);
1321
1322         return result;
1323 }
1324
1325 void conf_bridge_profile_copy(struct bridge_profile *dst, struct bridge_profile *src)
1326 {
1327         memcpy(dst, src, sizeof(*dst));
1328         if (src->sounds) {
1329                 ao2_ref(src->sounds, +1);
1330         }
1331 }
1332
1333 void conf_bridge_profile_destroy(struct bridge_profile *b_profile)
1334 {
1335         if (b_profile->sounds) {
1336                 ao2_ref(b_profile->sounds, -1);
1337                 b_profile->sounds = NULL;
1338         }
1339 }
1340
1341 const struct bridge_profile *conf_find_bridge_profile(struct ast_channel *chan, const char *bridge_profile_name, struct bridge_profile *result)
1342 {
1343         struct bridge_profile tmp;
1344         struct bridge_profile *tmp2;
1345         struct ast_datastore *datastore = NULL;
1346         struct func_confbridge_data *b_data = NULL;
1347
1348         if (chan) {
1349                 ast_channel_lock(chan);
1350                 if ((datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL))) {
1351                         ast_channel_unlock(chan);
1352                         b_data = datastore->data;
1353                         if (b_data->b_usable) {
1354                                 conf_bridge_profile_copy(result, &b_data->b_profile);
1355                                 return result;
1356                         }
1357                 }
1358                 ast_channel_unlock(chan);
1359         }
1360         if (ast_strlen_zero(bridge_profile_name)) {
1361                 bridge_profile_name = DEFAULT_BRIDGE_PROFILE;
1362         }
1363         ast_copy_string(tmp.name, bridge_profile_name, sizeof(tmp.name));
1364         if (!(tmp2 = ao2_find(bridge_profiles, &tmp, OBJ_POINTER))) {
1365                 return NULL;
1366         }
1367         ao2_lock(tmp2);
1368         conf_bridge_profile_copy(result, tmp2);
1369         ao2_unlock(tmp2);
1370         ao2_ref(tmp2, -1);
1371
1372         return result;
1373 }
1374
1375 struct dtmf_menu_hook_pvt {
1376         struct conference_bridge_user *conference_bridge_user;
1377         struct conf_menu_entry menu_entry;
1378         struct conf_menu *menu;
1379 };
1380
1381 static void menu_hook_destroy(void *hook_pvt)
1382 {
1383         struct dtmf_menu_hook_pvt *pvt = hook_pvt;
1384         struct conf_menu_action *action = NULL;
1385
1386         ao2_ref(pvt->menu, -1);
1387
1388         while ((action = AST_LIST_REMOVE_HEAD(&pvt->menu_entry.actions, action))) {
1389                 ast_free(action);
1390         }
1391         ast_free(pvt);
1392 }
1393
1394 static int menu_hook_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
1395 {
1396         struct dtmf_menu_hook_pvt *pvt = hook_pvt;
1397         return conf_handle_dtmf(bridge_channel, pvt->conference_bridge_user, &pvt->menu_entry, pvt->menu);
1398 }
1399
1400 static int copy_menu_entry(struct conf_menu_entry *dst, struct conf_menu_entry *src)
1401 {
1402         struct conf_menu_action *menu_action = NULL;
1403         struct conf_menu_action *new_menu_action = NULL;
1404
1405         memcpy(dst, src, sizeof(*dst));
1406         AST_LIST_HEAD_INIT_NOLOCK(&dst->actions);
1407
1408         AST_LIST_TRAVERSE(&src->actions, menu_action, action) {
1409                 if (!(new_menu_action = ast_calloc(1, sizeof(*new_menu_action)))) {
1410                         return -1;
1411                 }
1412                 memcpy(new_menu_action, menu_action, sizeof(*new_menu_action));
1413                 AST_LIST_INSERT_HEAD(&dst->actions, new_menu_action, action);
1414         }
1415         return 0;
1416 }
1417
1418 void conf_menu_entry_destroy(struct conf_menu_entry *menu_entry)
1419 {
1420         struct conf_menu_action *menu_action = NULL;
1421         while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
1422                 ast_free(menu_action);
1423         }
1424 }
1425
1426 int conf_find_menu_entry_by_sequence(const char *dtmf_sequence, struct conf_menu *menu, struct conf_menu_entry *result)
1427 {
1428         struct conf_menu_entry *menu_entry = NULL;
1429
1430         ao2_lock(menu);
1431         AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
1432                 if (!strcasecmp(menu_entry->dtmf, dtmf_sequence)) {
1433                         copy_menu_entry(result, menu_entry);
1434                         ao2_unlock(menu);
1435                         return 1;
1436                 }
1437         }
1438         ao2_unlock(menu);
1439
1440         return 0;
1441 }
1442
1443 int conf_set_menu_to_user(const char *menu_name, struct conference_bridge_user *conference_bridge_user)
1444 {
1445         struct conf_menu tmp;
1446         struct conf_menu *menu;
1447         struct conf_menu_entry *menu_entry = NULL;
1448         ast_copy_string(tmp.name, menu_name, sizeof(tmp.name));
1449
1450         if (!(menu = ao2_find(menus, &tmp, OBJ_POINTER))) {
1451                 return -1;
1452         }
1453         ao2_lock(menu);
1454         AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
1455                 struct dtmf_menu_hook_pvt *pvt;
1456                 if (!(pvt = ast_calloc(1, sizeof(*pvt)))) {
1457                         ao2_unlock(menu);
1458                         ao2_ref(menu, -1);
1459                         return -1;
1460                 }
1461                 if (copy_menu_entry(&pvt->menu_entry, menu_entry)) {
1462                         ast_free(pvt);
1463                         ao2_unlock(menu);
1464                         ao2_ref(menu, -1);
1465                         return -1;
1466                 }
1467                 pvt->conference_bridge_user = conference_bridge_user;
1468                 ao2_ref(menu, +1);
1469                 pvt->menu = menu;
1470
1471                 ast_bridge_features_hook(&conference_bridge_user->features, pvt->menu_entry.dtmf, menu_hook_callback, pvt, menu_hook_destroy);
1472         }
1473
1474         ao2_unlock(menu);
1475         ao2_ref(menu, -1);
1476
1477         return 0;
1478 }