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