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