2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2011, Digium, Inc.
6 * David Vossel <dvossel@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief ConfBridge config parser
23 * \author David Vossel <dvossel@digium.com>
27 <support_level>core</support_level>
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/bridge_features.h"
40 #include "asterisk/stringfields.h"
41 #include "asterisk/pbx.h"
45 <configInfo name="app_confbridge" language="en_US">
46 <synopsis>Conference Bridge Application</synopsis>
47 <configFile name="confbridge.conf">
48 <configObject name="global">
49 <synopsis>Unused, but reserved.</synopsis>
51 <configObject name="user_profile">
52 <synopsis>A named profile to apply to specific callers.</synopsis>
53 <description><para>Callers in a ConfBridge have a profile associated with them
54 that determine their options. A configuration section is determined to be a
55 user_profile when the <literal>type</literal> parameter has a value
56 of <literal>user</literal>.
58 <configOption name="type">
59 <synopsis>Define this configuration category as a user profile.</synopsis>
60 <description><para>The type parameter determines how a context in the
61 configuration file is interpreted.</para>
63 <enum name="user"><para>Configure the context as a <replaceable>user_profile</replaceable></para></enum>
64 <enum name="bridge"><para>Configure the context as a <replaceable>bridge_profile</replaceable></para></enum>
65 <enum name="menu"><para>Configure the context as a <replaceable>menu</replaceable></para></enum>
69 <configOption name="admin">
70 <synopsis>Sets if the user is an admin or not</synopsis>
72 <configOption name="marked">
73 <synopsis>Sets if this is a marked user or not</synopsis>
75 <configOption name="startmuted">
76 <synopsis>Sets if all users should start out muted</synopsis>
78 <configOption name="music_on_hold_when_empty">
79 <synopsis>Play MOH when user is alone or waiting on a marked user</synopsis>
81 <configOption name="quiet">
82 <synopsis>Silence enter/leave prompts and user intros for this user</synopsis>
84 <configOption name="announce_user_count">
85 <synopsis>Sets if the number of users should be announced to the user</synopsis>
87 <configOption name="announce_user_count_all">
88 <synopsis>Announce user count to all the other users when this user joins</synopsis>
89 <description><para>Sets if the number of users should be announced to all the other users
90 in the conference when this user joins. This option can be either set to 'yes' or
91 a number. When set to a number, the announcement will only occur once the user
92 count is above the specified number.
95 <configOption name="announce_only_user">
96 <synopsis>Announce to a user when they join an empty conference</synopsis>
98 <configOption name="wait_marked">
99 <synopsis>Sets if the user must wait for a marked user to enter before joining a conference</synopsis>
101 <configOption name="end_marked">
102 <synopsis>Kick the user from the conference when the last marked user leaves</synopsis>
104 <configOption name="talk_detection_events">
105 <synopsis>Set whether or not notifications of when a user begins and ends talking should be sent out as events over AMI</synopsis>
107 <configOption name="dtmf_passthrough">
108 <synopsis>Sets whether or not DTMF should pass through the conference</synopsis>
110 <configOption name="announce_join_leave">
111 <synopsis>Prompt user for their name when joining a conference and play it to the conference when they enter</synopsis>
113 <configOption name="pin">
114 <synopsis>Sets a PIN the user must enter before joining the conference</synopsis>
116 <configOption name="music_on_hold_class">
117 <synopsis>The MOH class to use for this user</synopsis>
119 <configOption name="announcement">
120 <synopsis>Sound file to play to the user when they join a conference</synopsis>
122 <configOption name="denoise">
123 <synopsis>Apply a denoise filter to the audio before mixing</synopsis>
124 <description><para>Sets whether or not a denoise filter should be applied
125 to the audio before mixing or not. Off by default. Requires
126 <literal>codec_speex</literal> to be built and installed. Do not confuse this option
127 with <replaceable>drop_silence</replaceable>. Denoise is useful if there is a lot of background
128 noise for a user as it attempts to remove the noise while preserving
129 the speech. This option does NOT remove silence from being mixed into
130 the conference and does come at the cost of a slight performance hit.
131 </para></description>
133 <configOption name="dsp_drop_silence">
134 <synopsis>Drop what Asterisk detects as silence from audio sent to the bridge</synopsis>
136 This option drops what Asterisk detects as silence from
137 entering into the bridge. Enabling this option will drastically
138 improve performance and help remove the buildup of background
139 noise from the conference. Highly recommended for large conferences
140 due to its performance enhancements.
141 </para></description>
143 <configOption name="dsp_silence_threshold">
144 <synopsis>The number of milliseconds of detected silence necessary to trigger silence detection</synopsis>
146 The time in milliseconds of sound falling within the what
147 the dsp has established as baseline silence before a user
148 is considered be silent. This value affects several
149 operations and should not be changed unless the impact
150 on call quality is fully understood.</para>
151 <para>What this value affects internally:</para>
153 1. When talk detection AMI events are enabled, this value
154 determines when the user has stopped talking after a
155 period of talking. If this value is set too low
156 AMI events indicating the user has stopped talking
157 may get falsely sent out when the user briefly pauses
161 2. The <replaceable>drop_silence</replaceable> option depends on this value to
162 determine when the user's audio should begin to be
163 dropped from the conference bridge after the user
164 stops talking. If this value is set too low the user's
165 audio stream may sound choppy to the other participants.
166 This is caused by the user transitioning constantly from
167 silence to talking during mid sentence.
170 The best way to approach this option is to set it slightly above
171 the maximum amount of ms of silence a user may generate during
174 <para>By default this value is 2500ms. Valid values are 1 through 2^31.</para>
177 <configOption name="dsp_talking_threshold">
178 <synopsis>The number of milliseconds of detected non-silence necessary to triger talk detection</synopsis>
180 The time in milliseconds of sound above what the dsp has
181 established as base line silence for a user before a user
182 is considered to be talking. This value affects several
183 operations and should not be changed unless the impact on
184 call quality is fully understood.</para>
186 What this value affects internally:
189 1. Audio is only mixed out of a user's incoming audio stream
190 if talking is detected. If this value is set too
191 loose the user will hear themselves briefly each
192 time they begin talking until the dsp has time to
193 establish that they are in fact talking.
196 2. When talk detection AMI events are enabled, this value
197 determines when talking has begun which results in
198 an AMI event to fire. If this value is set too tight
199 AMI events may be falsely triggered by variants in
203 3. The <replaceable>drop_silence</replaceable> option depends on this value to determine
204 when the user's audio should be mixed into the bridge
205 after periods of silence. If this value is too loose
206 the beginning of a user's speech will get cut off as they
207 transition from silence to talking.
209 <para>By default this value is 160 ms. Valid values are 1 through 2^31</para>
212 <configOption name="jitterbuffer">
213 <synopsis>Place a jitter buffer on the user's audio stream before audio mixing is performed</synopsis>
215 Enabling this option places a jitterbuffer on the user's audio stream
216 before audio mixing is performed. This is highly recommended but will
217 add a slight delay to the audio. This option is using the <literal>JITTERBUFFER</literal>
218 dialplan function's default adaptive jitterbuffer. For a more fine tuned
219 jitterbuffer, disable this option and use the <literal>JITTERBUFFER</literal> dialplan function
220 on the user before entering the ConfBridge application.
221 </para></description>
223 <configOption name="template">
224 <synopsis>When using the CONFBRIDGE dialplan function, use a user profile as a template for creating a new temporary profile</synopsis>
227 <configObject name="bridge_profile">
228 <synopsis>A named profile to apply to specific bridges.</synopsis>
229 <description><para>ConfBridge bridges have a profile associated with them
230 that determine their options. A configuration section is determined to be a
231 <literal>bridge_profile</literal> when the <literal>type</literal> parameter has a value
232 of <literal>bridge</literal>.
233 </para></description>
234 <configOption name="type">
235 <synopsis>Define this configuration category as a bridge profile</synopsis>
236 <description><para>The type parameter determines how a context in the
237 configuration file is interpreted.</para>
239 <enum name="user"><para>Configure the context as a <replaceable>user_profile</replaceable></para></enum>
240 <enum name="bridge"><para>Configure the context as a <replaceable>bridge_profile</replaceable></para></enum>
241 <enum name="menu"><para>Configure the context as a <replaceable>menu</replaceable></para></enum>
245 <configOption name="jitterbuffer">
246 <synopsis>Place a jitter buffer on the conference's audio stream</synopsis>
248 <configOption name="internal_sample_rate">
249 <synopsis>Set the internal native sample rate for mixing the conference</synopsis>
251 Sets the internal native sample rate the
252 conference is mixed at. This is set to automatically
253 adjust the sample rate to the best quality by default.
254 Other values can be anything from 8000-192000. If a
255 sample rate is set that Asterisk does not support, the
256 closest sample rate Asterisk does support to the one requested
258 </para></description>
260 <configOption name="mixing_interval">
261 <synopsis>Sets the internal mixing interval in milliseconds for the bridge</synopsis>
263 Sets the internal mixing interval in milliseconds for the bridge. This
264 number reflects how tight or loose the mixing will be for the conference.
265 In order to improve performance a larger mixing interval such as 40ms may
266 be chosen. Using a larger mixing interval comes at the cost of introducing
267 larger amounts of delay into the bridge. Valid values here are 10, 20, 40,
269 </para></description>
271 <configOption name="record_conference">
272 <synopsis>Record the conference starting with the first active user's entrance and ending with the last active user's exit</synopsis>
274 Records the conference call starting when the first user
275 enters the room, and ending when the last user exits the room.
276 The default recorded filename is
277 <filename>'confbridge-${name of conference bridge}-${start time}.wav'</filename>
278 and the default format is 8khz slinear. This file will be
279 located in the configured monitoring directory in <filename>asterisk.conf</filename>.
280 </para></description>
282 <configOption name="record_file" default="confbridge-${name of conference bridge}-${start time}.wav">
283 <synopsis>The filename of the conference recording</synopsis>
285 When <replaceable>record_conference</replaceable> is set to yes, the specific name of the
286 record file can be set using this option. Note that since multiple
287 conferences may use the same bridge profile, this may cause issues
288 depending on the configuration. It is recommended to only use this
289 option dynamically with the <literal>CONFBRIDGE()</literal> dialplan function. This
290 allows the record name to be specified and a unique name to be chosen.
291 By default, the record_file is stored in Asterisk's spool/monitor directory
292 with a unique filename starting with the 'confbridge' prefix.
293 </para></description>
295 <configOption name="record_file_append" default="yes">
296 <synopsis>Append record file when starting/stopping on same conference recording</synopsis>
298 When <replaceable>record_file_append</replaceable> is set to yes, stopping and starting recording on a
299 conference adds the new portion to end of current record_file. When this is
300 set to no, a new <replaceable>record_file</replaceable> is generated every time you start then stop recording
302 </para></description>
304 <configOption name="video_mode">
305 <synopsis>Sets how confbridge handles video distribution to the conference participants</synopsis>
307 Sets how confbridge handles video distribution to the conference participants.
308 Note that participants wanting to view and be the source of a video feed
309 <emphasis>MUST</emphasis> be sharing the same video codec. Also, using video in conjunction with
310 with the jitterbuffer currently results in the audio being slightly out of sync
311 with the video. This is a result of the jitterbuffer only working on the audio
312 stream. It is recommended to disable the jitterbuffer when video is used.</para>
315 <para>No video sources are set by default in the conference. It is still
316 possible for a user to be set as a video source via AMI or DTMF action
319 <enum name="follow_talker">
320 <para>The video feed will follow whoever is talking and providing video.</para>
322 <enum name="last_marked">
323 <para>The last marked user to join the conference with video capabilities
324 will be the single source of video distributed to all participants.
325 If multiple marked users are capable of video, the last one to join
326 is always the source, when that user leaves it goes to the one who
327 joined before them.</para>
329 <enum name="first_marked">
330 <para>The first marked user to join the conference with video capabilities
331 is the single source of video distribution among all participants. If
332 that user leaves, the marked user to join after them becomes the source.</para>
337 <configOption name="max_members">
338 <synopsis>Limit the maximum number of participants for a single conference</synopsis>
340 This option limits the number of participants for a single
341 conference to a specific number. By default conferences
342 have no participant limit. After the limit is reached, the
343 conference will be locked until someone leaves. Note however
344 that an Admin user will always be alowed to join the conference
345 regardless if this limit is reached or not.
346 </para></description>
348 <configOption name="^sound_">
349 <synopsis>Override the various conference bridge sound files</synopsis>
351 All sounds in the conference are customizable using the bridge profile options below.
352 Simply state the option followed by the filename or full path of the filename after
353 the option. Example: <literal>sound_had_joined=conf-hasjoin</literal> This will play the <literal>conf-hasjoin</literal>
354 sound file found in the sounds directory when announcing someone's name is joining the
357 <enum name="sound_join"><para>The sound played to everyone when someone enters the conference.</para></enum>
358 <enum name="sound_leave"><para>The sound played to everyone when someone leaves the conference.</para></enum>
359 <enum name="sound_has_joined"><para>The sound played before announcing someone's name has
360 joined the conference. This is used for user intros.
361 Example <literal>"_____ has joined the conference"</literal></para></enum>
362 <enum name="sound_has_left"><para>The sound played when announcing someone's name has
363 left the conference. This is used for user intros.
364 Example <literal>"_____ has left the conference"</literal></para></enum>
365 <enum name="sound_kicked"><para>The sound played to a user who has been kicked from the conference.</para></enum>
366 <enum name="sound_muted"><para>The sound played when the mute option it toggled on.</para></enum>
367 <enum name="sound_unmuted"><para>The sound played when the mute option it toggled off.</para></enum>
368 <enum name="sound_only_person"><para>The sound played when the user is the only person in the conference.</para></enum>
369 <enum name="sound_only_one"><para>The sound played to a user when there is only one other
370 person is in the conference.</para></enum>
371 <enum name="sound_there_are"><para>The sound played when announcing how many users there
372 are in a conference.</para></enum>
373 <enum name="sound_other_in_party"><para>This file is used in conjunction with <literal>sound_there_are</literal>
374 when announcing how many users there are in the conference.
375 The sounds are stringed together like this.
376 <literal>"sound_there_are" ${number of participants} "sound_other_in_party"</literal></para></enum>
377 <enum name="sound_place_into_conference"><para>The sound played when someone is placed into the conference
378 after waiting for a marked user.</para></enum>
379 <enum name="sound_wait_for_leader"><para>The sound played when a user is placed into a conference that
380 can not start until a marked user enters.</para></enum>
381 <enum name="sound_leader_has_left"><para>The sound played when the last marked user leaves the conference.</para></enum>
382 <enum name="sound_get_pin"><para>The sound played when prompting for a conference pin number.</para></enum>
383 <enum name="sound_invalid_pin"><para>The sound played when an invalid pin is entered too many times.</para></enum>
384 <enum name="sound_locked"><para>The sound played to a user trying to join a locked conference.</para></enum>
385 <enum name="sound_locked_now"><para>The sound played to an admin after toggling the conference to locked mode.</para></enum>
386 <enum name="sound_unlocked_now"><para>The sound played to an admin after toggling the conference to unlocked mode.</para></enum>
387 <enum name="sound_error_menu"><para>The sound played when an invalid menu option is entered.</para></enum>
391 <configOption name="template">
392 <synopsis>When using the CONFBRIDGE dialplan function, use a bridge profile as a template for creating a new temporary profile</synopsis>
395 <configObject name="menu">
396 <synopsis>A conference user menu</synopsis>
398 <para>Conference users, as defined by a <replaceable>conf_user</replaceable>,
399 can have a DTMF menu assigned to their profile when they enter the
400 <literal>ConfBridge</literal> application.</para>
402 <configOption name="type">
403 <synopsis>Define this configuration category as a menu</synopsis>
404 <description><para>The type parameter determines how a context in the
405 configuration file is interpreted.</para>
407 <enum name="user"><para>Configure the context as a <replaceable>user_profile</replaceable></para></enum>
408 <enum name="bridge"><para>Configure the context as a <replaceable>bridge_profile</replaceable></para></enum>
409 <enum name="menu"><para>Configure the context as a <replaceable>menu</replaceable></para></enum>
413 <configOption name="^[0-9A-D*#]+$">
414 <synopsis>DTMF sequences to assign various confbridge actions to</synopsis>
416 <para>The ConfBridge application also has the ability to apply custom DTMF menus to
417 each channel using the application. Like the User and Bridge profiles a menu
418 is passed in to ConfBridge as an argument in the dialplan.</para>
419 <para>Below is a list of menu actions that can be assigned to a DTMF sequence.</para>
421 A single DTMF sequence can have multiple actions associated with it. This is
422 accomplished by stringing the actions together and using a <literal>,</literal> as the
423 delimiter. Example: Both listening and talking volume is reset when <literal>5</literal> is
424 pressed. <literal>5=reset_talking_volume, reset_listening_volume</literal></para></note>
426 <enum name="playback(filename&filename2&...)"><para>
427 <literal>playback</literal> will play back an audio file to a channel
428 and then immediately return to the conference.
429 This file can not be interupted by DTMF.
430 Multiple files can be chained together using the
431 <literal>&</literal> character.</para></enum>
432 <enum name="playback_and_continue(filename&filename2&...)"><para>
433 <literal>playback_and_continue</literal> will
434 play back a prompt while continuing to
435 collect the dtmf sequence. This is useful
436 when using a menu prompt that describes all
437 the menu options. Note however that any DTMF
438 during this action will terminate the prompts
439 playback. Prompt files can be chained together
440 using the <literal>&</literal> character as a delimiter.</para></enum>
441 <enum name="toggle_mute"><para>
442 Toggle turning on and off mute. Mute will make the user silent
443 to everyone else, but the user will still be able to listen in.
444 continue to collect the dtmf sequence.</para></enum>
445 <enum name="no_op"><para>
446 This action does nothing (No Operation). Its only real purpose exists for
447 being able to reserve a sequence in the config as a menu exit sequence.</para></enum>
448 <enum name="decrease_listening_volume"><para>
449 Decreases the channel's listening volume.</para></enum>
450 <enum name="increase_listening_volume"><para>
451 Increases the channel's listening volume.</para></enum>
452 <enum name="reset_listening_volume"><para>
453 Reset channel's listening volume to default level.</para></enum>
454 <enum name="decrease_talking_volume"><para>
455 Decreases the channel's talking volume.</para></enum>
456 <enum name="increase_talking_volume"><para>
457 Increases the channel's talking volume.</para></enum>
458 <enum name="reset_talking_volume"><para>
459 Reset channel's talking volume to default level.</para></enum>
460 <enum name="dialplan_exec(context,exten,priority)"><para>
461 The <literal>dialplan_exec</literal> action allows a user
462 to escape from the conference and execute
463 commands in the dialplan. Once the dialplan
464 exits the user will be put back into the
465 conference. The possibilities are endless!</para></enum>
466 <enum name="leave_conference"><para>
467 This action allows a user to exit the conference and continue
468 execution in the dialplan.</para></enum>
469 <enum name="admin_kick_last"><para>
470 This action allows an Admin to kick the last participant from the
471 conference. This action will only work for admins which allows
472 a single menu to be used for both users and admins.</para></enum>
473 <enum name="admin_toggle_conference_lock"><para>
474 This action allows an Admin to toggle locking and
475 unlocking the conference. Non admins can not use
476 this action even if it is in their menu.</para></enum>
477 <enum name="set_as_single_video_src"><para>
478 This action allows any user to set themselves as the
479 single video source distributed to all participants.
480 This will make the video feed stick to them regardless
481 of what the <literal>video_mode</literal> is set to.</para></enum>
482 <enum name="release_as_single_video_src"><para>
483 This action allows a user to release themselves as
484 the video source. If <literal>video_mode</literal> is not set to <literal>none</literal>
485 this action will result in the conference returning to
486 whatever video mode the bridge profile is using.</para>
487 <para>Note that this action will have no effect if the user
488 is not currently the video source. Also, the user is
489 not guaranteed by using this action that they will not
490 become the video source again. The bridge will return
491 to whatever operation the <literal>video_mode</literal> option is set to
492 upon release of the video src.</para></enum>
493 <enum name="admin_toggle_mute_participants"><para>
494 This action allows an administrator to toggle the mute
495 state for all non-admins within a conference. All
496 admin users are unaffected by this option. Note that all
497 users, regardless of their admin status, are notified
498 that the conference is muted.</para></enum>
499 <enum name="participant_count"><para>
500 This action plays back the number of participants currently
501 in a conference</para></enum>
510 struct confbridge_cfg {
511 struct ao2_container *bridge_profiles;
512 struct ao2_container *user_profiles;
513 struct ao2_container *menus;
516 static int verify_default_profiles(void);
517 static void *bridge_profile_alloc(const char *category);
518 static void *bridge_profile_find(struct ao2_container *container, const char *category);
519 static struct bridge_profile_sounds *bridge_profile_sounds_alloc(void);
521 static void bridge_profile_destructor(void *obj)
523 struct bridge_profile *b_profile = obj;
524 ao2_cleanup(b_profile->sounds);
527 static void *bridge_profile_alloc(const char *category)
529 struct bridge_profile *b_profile;
531 if (!(b_profile = ao2_alloc(sizeof(*b_profile), bridge_profile_destructor))) {
535 if (!(b_profile->sounds = bridge_profile_sounds_alloc())) {
536 ao2_ref(b_profile, -1);
540 ast_copy_string(b_profile->name, category, sizeof(b_profile->name));
545 static void *bridge_profile_find(struct ao2_container *container, const char *category)
547 return ao2_find(container, category, OBJ_KEY);
550 static struct aco_type bridge_type = {
552 .name = "bridge_profile",
553 .category_match = ACO_BLACKLIST,
554 .category = "^general$",
555 .matchfield = "type",
556 .matchvalue = "bridge",
557 .item_alloc = bridge_profile_alloc,
558 .item_find = bridge_profile_find,
559 .item_offset = offsetof(struct confbridge_cfg, bridge_profiles),
562 static void *user_profile_alloc(const char *category);
563 static void *user_profile_find(struct ao2_container *container, const char *category);
564 static void user_profile_destructor(void *obj)
569 static void *user_profile_alloc(const char *category)
571 struct user_profile *u_profile;
573 if (!(u_profile = ao2_alloc(sizeof(*u_profile), user_profile_destructor))) {
577 ast_copy_string(u_profile->name, category, sizeof(u_profile->name));
582 static void *user_profile_find(struct ao2_container *container, const char *category)
584 return ao2_find(container, category, OBJ_KEY);
587 static struct aco_type user_type = {
589 .name = "user_profile",
590 .category_match = ACO_BLACKLIST,
591 .category = "^general$",
592 .matchfield = "type",
593 .matchvalue = "user",
594 .item_alloc = user_profile_alloc,
595 .item_find = user_profile_find,
596 .item_offset = offsetof(struct confbridge_cfg, user_profiles),
599 static void *menu_alloc(const char *category);
600 static void *menu_find(struct ao2_container *container, const char *category);
601 static void menu_destructor(void *obj);
603 static void *menu_alloc(const char *category)
605 struct conf_menu *menu;
606 if (!(menu = ao2_alloc(sizeof(*menu), menu_destructor))) {
609 ast_copy_string(menu->name, category, sizeof(menu->name));
613 static void *menu_find(struct ao2_container *container, const char *category)
615 return ao2_find(container, category, OBJ_KEY);
618 static struct aco_type menu_type = {
621 .category_match = ACO_BLACKLIST,
622 .category = "^general$",
623 .matchfield = "type",
624 .matchvalue = "menu",
625 .item_alloc = menu_alloc,
626 .item_find = menu_find,
627 .item_offset = offsetof(struct confbridge_cfg, menus),
630 /* Used to pass to aco_option_register */
631 static struct aco_type *bridge_types[] = ACO_TYPES(&bridge_type);
632 static struct aco_type *menu_types[] = ACO_TYPES(&menu_type);
633 static struct aco_type *user_types[] = ACO_TYPES(&user_type);
635 /* The general category is reserved, but unused */
636 static struct aco_type general_type = {
639 .category_match = ACO_WHITELIST,
640 .category = "^general$",
643 static struct aco_file confbridge_conf = {
644 .filename = "confbridge.conf",
645 .types = ACO_TYPES(&bridge_type, &user_type, &menu_type, &general_type),
648 static AO2_GLOBAL_OBJ_STATIC(cfg_handle);
650 static void *confbridge_cfg_alloc(void);
652 CONFIG_INFO_STANDARD(cfg_info, cfg_handle, confbridge_cfg_alloc,
653 .files = ACO_FILES(&confbridge_conf),
654 .pre_apply_config = verify_default_profiles,
657 /*! bridge profile container functions */
658 static int bridge_cmp_cb(void *obj, void *arg, int flags)
660 const struct bridge_profile *left = obj;
661 const struct bridge_profile *right = arg;
662 const char *right_name = arg;
665 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
668 right_name = right->name;
671 cmp = strcasecmp(left->name, right_name);
673 case OBJ_PARTIAL_KEY:
674 cmp = strncasecmp(left->name, right_name, strlen(right_name));
677 return cmp ? 0 : CMP_MATCH;
680 static int bridge_hash_cb(const void *obj, const int flags)
682 const struct bridge_profile *b_profile = obj;
683 const char *name = obj;
686 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
689 name = b_profile->name;
692 hash = ast_str_case_hash(name);
694 case OBJ_PARTIAL_KEY:
695 /* Should never happen in hash callback. */
703 /*! menu container functions */
704 static int menu_cmp_cb(void *obj, void *arg, int flags)
706 const struct conf_menu *left = obj;
707 const struct conf_menu *right = arg;
708 const char *right_name = arg;
711 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
714 right_name = right->name;
717 cmp = strcasecmp(left->name, right_name);
719 case OBJ_PARTIAL_KEY:
720 cmp = strncasecmp(left->name, right_name, strlen(right_name));
723 return cmp ? 0 : CMP_MATCH;
726 static int menu_hash_cb(const void *obj, const int flags)
728 const struct conf_menu *menu = obj;
729 const char *name = obj;
732 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
738 hash = ast_str_case_hash(name);
740 case OBJ_PARTIAL_KEY:
741 /* Should never happen in hash callback. */
749 static void menu_destructor(void *obj)
751 struct conf_menu *menu = obj;
752 struct conf_menu_entry *entry = NULL;
754 while ((entry = AST_LIST_REMOVE_HEAD(&menu->entries, entry))) {
755 conf_menu_entry_destroy(entry);
760 /*! User profile container functions */
761 static int user_cmp_cb(void *obj, void *arg, int flags)
763 const struct user_profile *left = obj;
764 const struct user_profile *right = arg;
765 const char *right_name = arg;
768 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
771 right_name = right->name;
774 cmp = strcasecmp(left->name, right_name);
776 case OBJ_PARTIAL_KEY:
777 cmp = strncasecmp(left->name, right_name, strlen(right_name));
780 return cmp ? 0 : CMP_MATCH;
783 static int user_hash_cb(const void *obj, const int flags)
785 const struct user_profile *u_profile = obj;
786 const char *name = obj;
789 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
792 name = u_profile->name;
795 hash = ast_str_case_hash(name);
797 case OBJ_PARTIAL_KEY:
798 /* Should never happen in hash callback. */
806 /*! Bridge Profile Sounds functions */
807 static void bridge_profile_sounds_destroy_cb(void *obj)
809 struct bridge_profile_sounds *sounds = obj;
810 ast_string_field_free_memory(sounds);
813 static struct bridge_profile_sounds *bridge_profile_sounds_alloc(void)
815 struct bridge_profile_sounds *sounds = ao2_alloc(sizeof(*sounds), bridge_profile_sounds_destroy_cb);
820 if (ast_string_field_init(sounds, 512)) {
828 static int set_sound(const char *sound_name, const char *sound_file, struct bridge_profile *b_profile)
830 struct bridge_profile_sounds *sounds = b_profile->sounds;
831 if (ast_strlen_zero(sound_file)) {
835 if (!strcasecmp(sound_name, "sound_only_person")) {
836 ast_string_field_set(sounds, onlyperson, sound_file);
837 } else if (!strcasecmp(sound_name, "sound_only_one")) {
838 ast_string_field_set(sounds, onlyone, sound_file);
839 } else if (!strcasecmp(sound_name, "sound_has_joined")) {
840 ast_string_field_set(sounds, hasjoin, sound_file);
841 } else if (!strcasecmp(sound_name, "sound_has_left")) {
842 ast_string_field_set(sounds, hasleft, sound_file);
843 } else if (!strcasecmp(sound_name, "sound_kicked")) {
844 ast_string_field_set(sounds, kicked, sound_file);
845 } else if (!strcasecmp(sound_name, "sound_muted")) {
846 ast_string_field_set(sounds, muted, sound_file);
847 } else if (!strcasecmp(sound_name, "sound_unmuted")) {
848 ast_string_field_set(sounds, unmuted, sound_file);
849 } else if (!strcasecmp(sound_name, "sound_there_are")) {
850 ast_string_field_set(sounds, thereare, sound_file);
851 } else if (!strcasecmp(sound_name, "sound_other_in_party")) {
852 ast_string_field_set(sounds, otherinparty, sound_file);
853 } else if (!strcasecmp(sound_name, "sound_place_into_conference")) {
854 ast_string_field_set(sounds, placeintoconf, sound_file);
855 } else if (!strcasecmp(sound_name, "sound_wait_for_leader")) {
856 ast_string_field_set(sounds, waitforleader, sound_file);
857 } else if (!strcasecmp(sound_name, "sound_leader_has_left")) {
858 ast_string_field_set(sounds, leaderhasleft, sound_file);
859 } else if (!strcasecmp(sound_name, "sound_get_pin")) {
860 ast_string_field_set(sounds, getpin, sound_file);
861 } else if (!strcasecmp(sound_name, "sound_invalid_pin")) {
862 ast_string_field_set(sounds, invalidpin, sound_file);
863 } else if (!strcasecmp(sound_name, "sound_locked")) {
864 ast_string_field_set(sounds, locked, sound_file);
865 } else if (!strcasecmp(sound_name, "sound_unlocked_now")) {
866 ast_string_field_set(sounds, unlockednow, sound_file);
867 } else if (!strcasecmp(sound_name, "sound_locked_now")) {
868 ast_string_field_set(sounds, lockednow, sound_file);
869 } else if (!strcasecmp(sound_name, "sound_error_menu")) {
870 ast_string_field_set(sounds, errormenu, sound_file);
871 } else if (!strcasecmp(sound_name, "sound_join")) {
872 ast_string_field_set(sounds, join, sound_file);
873 } else if (!strcasecmp(sound_name, "sound_leave")) {
874 ast_string_field_set(sounds, leave, sound_file);
875 } else if (!strcasecmp(sound_name, "sound_participants_muted")) {
876 ast_string_field_set(sounds, participantsmuted, sound_file);
877 } else if (!strcasecmp(sound_name, "sound_participants_unmuted")) {
878 ast_string_field_set(sounds, participantsunmuted, sound_file);
886 /*! CONFBRIDGE dialplan function functions and channel datastore. */
887 struct func_confbridge_data {
888 struct bridge_profile b_profile;
889 struct user_profile u_profile;
890 unsigned int b_usable:1; /*!< Tells if bridge profile is usable or not */
891 unsigned int u_usable:1; /*!< Tells if user profile is usable or not */
893 static void func_confbridge_destroy_cb(void *data)
895 struct func_confbridge_data *b_data = data;
896 conf_bridge_profile_destroy(&b_data->b_profile);
899 static const struct ast_datastore_info confbridge_datastore = {
900 .type = "confbridge",
901 .destroy = func_confbridge_destroy_cb
903 int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value)
905 struct ast_datastore *datastore;
906 struct func_confbridge_data *b_data;
908 struct ast_variable tmpvar = { 0, };
909 AST_DECLARE_APP_ARGS(args,
914 /* parse all the required arguments and make sure they exist. */
915 if (ast_strlen_zero(data)) {
918 parse = ast_strdupa(data);
919 AST_STANDARD_APP_ARGS(args, parse);
920 if (ast_strlen_zero(args.type) || ast_strlen_zero(args.option)) {
924 ast_channel_lock(chan);
925 datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
927 datastore = ast_datastore_alloc(&confbridge_datastore, NULL);
929 ast_channel_unlock(chan);
932 b_data = ast_calloc(1, sizeof(*b_data));
934 ast_channel_unlock(chan);
935 ast_datastore_free(datastore);
938 b_data->b_profile.sounds = bridge_profile_sounds_alloc();
939 if (!b_data->b_profile.sounds) {
940 ast_channel_unlock(chan);
941 ast_datastore_free(datastore);
945 datastore->data = b_data;
946 ast_channel_datastore_add(chan, datastore);
948 b_data = datastore->data;
950 ast_channel_unlock(chan);
952 /* SET(CONFBRIDGE(type,option)=value) */
956 tmpvar.name = args.option;
957 tmpvar.value = value;
958 tmpvar.file = "CONFBRIDGE";
959 if (!strcasecmp(args.type, "bridge")) {
960 if (!aco_process_var(&bridge_type, "dialplan", &tmpvar, &b_data->b_profile)) {
961 b_data->b_usable = 1;
964 } else if (!strcasecmp(args.type, "user")) {
965 if (!aco_process_var(&user_type, "dialplan", &tmpvar, &b_data->u_profile)) {
966 b_data->u_usable = 1;
971 ast_log(LOG_WARNING, "%s(%s,%s) cannot be set to '%s'. Invalid type, option, or value.\n",
972 cmd, args.type, args.option, value);
976 static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum conf_menu_action_id id, char *databuf)
978 struct conf_menu_action *menu_action = ast_calloc(1, sizeof(*menu_action));
983 menu_action->id = id;
986 case MENU_ACTION_NOOP:
987 case MENU_ACTION_TOGGLE_MUTE:
988 case MENU_ACTION_INCREASE_LISTENING:
989 case MENU_ACTION_DECREASE_LISTENING:
990 case MENU_ACTION_INCREASE_TALKING:
991 case MENU_ACTION_DECREASE_TALKING:
992 case MENU_ACTION_RESET_LISTENING:
993 case MENU_ACTION_RESET_TALKING:
994 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
995 case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
996 case MENU_ACTION_PARTICIPANT_COUNT:
997 case MENU_ACTION_ADMIN_KICK_LAST:
998 case MENU_ACTION_LEAVE:
999 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
1000 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
1002 case MENU_ACTION_PLAYBACK:
1003 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
1004 if (!(ast_strlen_zero(databuf))) {
1005 ast_copy_string(menu_action->data.playback_file, databuf, sizeof(menu_action->data.playback_file));
1007 ast_free(menu_action);
1011 case MENU_ACTION_DIALPLAN_EXEC:
1012 if (!(ast_strlen_zero(databuf))) {
1013 AST_DECLARE_APP_ARGS(args,
1014 AST_APP_ARG(context);
1016 AST_APP_ARG(priority);
1018 AST_STANDARD_APP_ARGS(args, databuf);
1019 if (!ast_strlen_zero(args.context)) {
1020 ast_copy_string(menu_action->data.dialplan_args.context,
1022 sizeof(menu_action->data.dialplan_args.context));
1024 if (!ast_strlen_zero(args.exten)) {
1025 ast_copy_string(menu_action->data.dialplan_args.exten,
1027 sizeof(menu_action->data.dialplan_args.exten));
1029 menu_action->data.dialplan_args.priority = 1; /* 1 by default */
1030 if (!ast_strlen_zero(args.priority) &&
1031 (sscanf(args.priority, "%30u", &menu_action->data.dialplan_args.priority) != 1)) {
1032 /* invalid priority */
1033 ast_free(menu_action);
1037 ast_free(menu_action);
1042 AST_LIST_INSERT_TAIL(&menu_entry->actions, menu_action, action);
1047 static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *action_names)
1049 struct conf_menu_entry *menu_entry = NULL, *cur = NULL;
1051 char *tmp_action_names = ast_strdupa(action_names);
1052 char *action = NULL;
1056 char *delimiter = ",";
1058 if (!(menu_entry = ast_calloc(1, sizeof(*menu_entry)))) {
1066 unsigned int action_len;
1068 if (ast_strlen_zero(tmp_action_names)) {
1071 startbrace = strchr(tmp_action_names, '(');
1072 endbrace = strchr(tmp_action_names, ')');
1073 comma = strchr(tmp_action_names, ',');
1075 /* If the next action has brackets with comma delimited arguments in it,
1076 * make the delimeter ')' instead of a comma to preserve the argments */
1077 if (startbrace && endbrace && comma && (comma > startbrace && comma < endbrace)) {
1083 if (!(action = strsep(&tmp_action_names, delimiter))) {
1087 action = ast_strip(action);
1088 if (ast_strlen_zero(action)) {
1092 action_len = strlen(action);
1093 ast_copy_string(menu_entry->dtmf, dtmf, sizeof(menu_entry->dtmf));
1094 if (!strcasecmp(action, "toggle_mute")) {
1095 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_TOGGLE_MUTE, NULL);
1096 } else if (!strcasecmp(action, "no_op")) {
1097 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_NOOP, NULL);
1098 } else if (!strcasecmp(action, "increase_listening_volume")) {
1099 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_LISTENING, NULL);
1100 } else if (!strcasecmp(action, "decrease_listening_volume")) {
1101 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_LISTENING, NULL);
1102 } else if (!strcasecmp(action, "increase_talking_volume")) {
1103 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_TALKING, NULL);
1104 } else if (!strcasecmp(action, "reset_listening_volume")) {
1105 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_LISTENING, NULL);
1106 } else if (!strcasecmp(action, "reset_talking_volume")) {
1107 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_TALKING, NULL);
1108 } else if (!strcasecmp(action, "decrease_talking_volume")) {
1109 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_TALKING, NULL);
1110 } else if (!strcasecmp(action, "admin_toggle_conference_lock")) {
1111 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_LOCK, NULL);
1112 } else if (!strcasecmp(action, "admin_toggle_mute_participants")) {
1113 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS, NULL);
1114 } else if (!strcasecmp(action, "participant_count")) {
1115 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PARTICIPANT_COUNT, NULL);
1116 } else if (!strcasecmp(action, "admin_kick_last")) {
1117 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_KICK_LAST, NULL);
1118 } else if (!strcasecmp(action, "leave_conference")) {
1119 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_LEAVE, NULL);
1120 } else if (!strcasecmp(action, "set_as_single_video_src")) {
1121 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_SET_SINGLE_VIDEO_SRC, NULL);
1122 } else if (!strcasecmp(action, "release_as_single_video_src")) {
1123 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC, NULL);
1124 } else if (!strncasecmp(action, "dialplan_exec(", 14)) {
1125 ast_copy_string(buf, action, sizeof(buf));
1127 if ((action_args = strchr(action, '('))) {
1130 /* it is possible that this argument may or may not
1131 * have a closing brace at this point, it all depends on if
1132 * comma delimited arguments were provided */
1133 if ((tmp = strchr(action, ')'))) {
1136 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DIALPLAN_EXEC, action_args);
1137 } else if (action_len >= 21 && !strncasecmp(action, "playback_and_continue(", 22)) {
1138 ast_copy_string(buf, action, sizeof(buf));
1140 if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
1144 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK_AND_CONTINUE, action_args);
1145 } else if (action_len >= 8 && !strncasecmp(action, "playback(", 9)) {
1146 ast_copy_string(buf, action, sizeof(buf));
1148 if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
1152 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK, action_args);
1156 /* if adding any of the actions failed, bail */
1158 struct conf_menu_action *menu_action;
1159 while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
1160 ast_free(menu_action);
1162 ast_free(menu_entry);
1166 /* remove any list entry with an identical DTMF sequence for overrides */
1167 AST_LIST_TRAVERSE_SAFE_BEGIN(&menu->entries, cur, entry) {
1168 if (!strcasecmp(cur->dtmf, menu_entry->dtmf)) {
1169 AST_LIST_REMOVE_CURRENT(entry);
1174 AST_LIST_TRAVERSE_SAFE_END;
1176 AST_LIST_INSERT_TAIL(&menu->entries, menu_entry, entry);
1181 static char *complete_user_profile_name(const char *line, const char *word, int pos, int state)
1185 int wordlen = strlen(word);
1186 struct ao2_iterator i;
1187 struct user_profile *u_profile = NULL;
1188 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
1194 i = ao2_iterator_init(cfg->user_profiles, 0);
1195 while ((u_profile = ao2_iterator_next(&i))) {
1196 if (!strncasecmp(u_profile->name, word, wordlen) && ++which > state) {
1197 res = ast_strdup(u_profile->name);
1198 ao2_ref(u_profile, -1);
1201 ao2_ref(u_profile, -1);
1203 ao2_iterator_destroy(&i);
1208 static char *handle_cli_confbridge_show_user_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1210 struct ao2_iterator it;
1211 struct user_profile *u_profile;
1212 RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
1216 e->command = "confbridge show profile users";
1218 "Usage confbridge show profile users\n";
1224 if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
1228 ast_cli(a->fd,"--------- User Profiles -----------\n");
1229 ao2_lock(cfg->user_profiles);
1230 it = ao2_iterator_init(cfg->user_profiles, 0);
1231 while ((u_profile = ao2_iterator_next(&it))) {
1232 ast_cli(a->fd,"%s\n", u_profile->name);
1233 ao2_ref(u_profile, -1);
1235 ao2_iterator_destroy(&it);
1236 ao2_unlock(cfg->user_profiles);
1240 static char *handle_cli_confbridge_show_user_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1242 struct user_profile u_profile;
1246 e->command = "confbridge show profile user";
1248 "Usage confbridge show profile user [<profile name>]\n";
1252 return complete_user_profile_name(a->line, a->word, a->pos, a->n);
1258 return CLI_SHOWUSAGE;
1261 if (!(conf_find_user_profile(NULL, a->argv[4], &u_profile))) {
1262 ast_cli(a->fd, "No conference user profile named '%s' found!\n", a->argv[4]);
1266 ast_cli(a->fd,"--------------------------------------------\n");
1267 ast_cli(a->fd,"Name: %s\n",
1269 ast_cli(a->fd,"Admin: %s\n",
1270 u_profile.flags & USER_OPT_ADMIN ?
1272 ast_cli(a->fd,"Marked User: %s\n",
1273 u_profile.flags & USER_OPT_MARKEDUSER ?
1275 ast_cli(a->fd,"Start Muted: %s\n",
1276 u_profile.flags & USER_OPT_STARTMUTED?
1278 ast_cli(a->fd,"MOH When Empty: %s\n",
1279 u_profile.flags & USER_OPT_MUSICONHOLD ?
1280 "enabled" : "disabled");
1281 ast_cli(a->fd,"MOH Class: %s\n",
1282 ast_strlen_zero(u_profile.moh_class) ?
1283 "default" : u_profile.moh_class);
1284 ast_cli(a->fd,"Announcement: %s\n",
1285 u_profile.announcement);
1286 ast_cli(a->fd,"Quiet: %s\n",
1287 u_profile.flags & USER_OPT_QUIET ?
1288 "enabled" : "disabled");
1289 ast_cli(a->fd,"Wait Marked: %s\n",
1290 u_profile.flags & USER_OPT_WAITMARKED ?
1291 "enabled" : "disabled");
1292 ast_cli(a->fd,"END Marked: %s\n",
1293 u_profile.flags & USER_OPT_ENDMARKED ?
1294 "enabled" : "disabled");
1295 ast_cli(a->fd,"Drop_silence: %s\n",
1296 u_profile.flags & USER_OPT_DROP_SILENCE ?
1297 "enabled" : "disabled");
1298 ast_cli(a->fd,"Silence Threshold: %dms\n",
1299 u_profile.silence_threshold);
1300 ast_cli(a->fd,"Talking Threshold: %dms\n",
1301 u_profile.talking_threshold);
1302 ast_cli(a->fd,"Denoise: %s\n",
1303 u_profile.flags & USER_OPT_DENOISE ?
1304 "enabled" : "disabled");
1305 ast_cli(a->fd,"Jitterbuffer: %s\n",
1306 u_profile.flags & USER_OPT_JITTERBUFFER ?
1307 "enabled" : "disabled");
1308 ast_cli(a->fd,"Talk Detect Events: %s\n",
1309 u_profile.flags & USER_OPT_TALKER_DETECT ?
1310 "enabled" : "disabled");
1311 ast_cli(a->fd,"DTMF Pass Through: %s\n",
1312 u_profile.flags & USER_OPT_DTMF_PASS ?
1313 "enabled" : "disabled");
1314 ast_cli(a->fd,"PIN: %s\n",
1315 ast_strlen_zero(u_profile.pin) ?
1316 "None" : u_profile.pin);
1317 ast_cli(a->fd,"Announce User Count: %s\n",
1318 u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNT ?
1319 "enabled" : "disabled");
1320 ast_cli(a->fd,"Announce join/leave: %s\n",
1321 u_profile.flags & USER_OPT_ANNOUNCE_JOIN_LEAVE ?
1322 "enabled" : "disabled");
1323 ast_cli(a->fd,"Announce User Count all: %s\n",
1324 u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNTALL ?
1325 "enabled" : "disabled");
1326 ast_cli(a->fd,"\n");
1331 static char *complete_bridge_profile_name(const char *line, const char *word, int pos, int state)
1335 int wordlen = strlen(word);
1336 struct ao2_iterator i;
1337 struct bridge_profile *b_profile = NULL;
1338 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
1344 i = ao2_iterator_init(cfg->bridge_profiles, 0);
1345 while ((b_profile = ao2_iterator_next(&i))) {
1346 if (!strncasecmp(b_profile->name, word, wordlen) && ++which > state) {
1347 res = ast_strdup(b_profile->name);
1348 ao2_ref(b_profile, -1);
1351 ao2_ref(b_profile, -1);
1353 ao2_iterator_destroy(&i);
1358 static char *handle_cli_confbridge_show_bridge_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1360 struct ao2_iterator it;
1361 struct bridge_profile *b_profile;
1362 RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
1366 e->command = "confbridge show profile bridges";
1368 "Usage confbridge show profile bridges\n";
1374 if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
1378 ast_cli(a->fd,"--------- Bridge Profiles -----------\n");
1379 ao2_lock(cfg->bridge_profiles);
1380 it = ao2_iterator_init(cfg->bridge_profiles, 0);
1381 while ((b_profile = ao2_iterator_next(&it))) {
1382 ast_cli(a->fd,"%s\n", b_profile->name);
1383 ao2_ref(b_profile, -1);
1385 ao2_iterator_destroy(&it);
1386 ao2_unlock(cfg->bridge_profiles);
1391 static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1393 struct bridge_profile b_profile;
1398 e->command = "confbridge show profile bridge";
1400 "Usage confbridge show profile bridge <profile name>\n";
1404 return complete_bridge_profile_name(a->line, a->word, a->pos, a->n);
1410 return CLI_SHOWUSAGE;
1413 if (!(conf_find_bridge_profile(NULL, a->argv[4], &b_profile))) {
1414 ast_cli(a->fd, "No conference bridge profile named '%s' found!\n", a->argv[4]);
1418 ast_cli(a->fd,"--------------------------------------------\n");
1419 ast_cli(a->fd,"Name: %s\n", b_profile.name);
1421 if (b_profile.internal_sample_rate) {
1422 snprintf(tmp, sizeof(tmp), "%d", b_profile.internal_sample_rate);
1424 ast_copy_string(tmp, "auto", sizeof(tmp));
1426 ast_cli(a->fd,"Internal Sample Rate: %s\n", tmp);
1428 if (b_profile.mix_interval) {
1429 ast_cli(a->fd,"Mixing Interval: %d\n", b_profile.mix_interval);
1431 ast_cli(a->fd,"Mixing Interval: Default 20ms\n");
1434 ast_cli(a->fd,"Record Conference: %s\n",
1435 b_profile.flags & BRIDGE_OPT_RECORD_CONFERENCE ?
1438 ast_cli(a->fd,"Record File Append: %s\n",
1439 b_profile.flags & BRIDGE_OPT_RECORD_FILE_APPEND ?
1442 ast_cli(a->fd,"Record File: %s\n",
1443 ast_strlen_zero(b_profile.rec_file) ? "Auto Generated" :
1444 b_profile.rec_file);
1446 if (b_profile.max_members) {
1447 ast_cli(a->fd,"Max Members: %d\n", b_profile.max_members);
1449 ast_cli(a->fd,"Max Members: No Limit\n");
1452 switch (b_profile.flags
1453 & (BRIDGE_OPT_VIDEO_SRC_LAST_MARKED | BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
1454 | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
1455 case BRIDGE_OPT_VIDEO_SRC_LAST_MARKED:
1456 ast_cli(a->fd, "Video Mode: last_marked\n");
1458 case BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED:
1459 ast_cli(a->fd, "Video Mode: first_marked\n");
1461 case BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER:
1462 ast_cli(a->fd, "Video Mode: follow_talker\n");
1465 ast_cli(a->fd, "Video Mode: no video\n");
1468 /* Opps. We have more than one video mode flag set. */
1473 ast_cli(a->fd,"sound_only_person: %s\n", conf_get_sound(CONF_SOUND_ONLY_PERSON, b_profile.sounds));
1474 ast_cli(a->fd,"sound_only_one: %s\n", conf_get_sound(CONF_SOUND_ONLY_ONE, b_profile.sounds));
1475 ast_cli(a->fd,"sound_has_joined: %s\n", conf_get_sound(CONF_SOUND_HAS_JOINED, b_profile.sounds));
1476 ast_cli(a->fd,"sound_has_left: %s\n", conf_get_sound(CONF_SOUND_HAS_LEFT, b_profile.sounds));
1477 ast_cli(a->fd,"sound_kicked: %s\n", conf_get_sound(CONF_SOUND_KICKED, b_profile.sounds));
1478 ast_cli(a->fd,"sound_muted: %s\n", conf_get_sound(CONF_SOUND_MUTED, b_profile.sounds));
1479 ast_cli(a->fd,"sound_unmuted: %s\n", conf_get_sound(CONF_SOUND_UNMUTED, b_profile.sounds));
1480 ast_cli(a->fd,"sound_there_are: %s\n", conf_get_sound(CONF_SOUND_THERE_ARE, b_profile.sounds));
1481 ast_cli(a->fd,"sound_other_in_party: %s\n", conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, b_profile.sounds));
1482 ast_cli(a->fd,"sound_place_into_conference: %s\n", conf_get_sound(CONF_SOUND_PLACE_IN_CONF, b_profile.sounds));
1483 ast_cli(a->fd,"sound_wait_for_leader: %s\n", conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, b_profile.sounds));
1484 ast_cli(a->fd,"sound_leader_has_left: %s\n", conf_get_sound(CONF_SOUND_LEADER_HAS_LEFT, b_profile.sounds));
1485 ast_cli(a->fd,"sound_get_pin: %s\n", conf_get_sound(CONF_SOUND_GET_PIN, b_profile.sounds));
1486 ast_cli(a->fd,"sound_invalid_pin: %s\n", conf_get_sound(CONF_SOUND_INVALID_PIN, b_profile.sounds));
1487 ast_cli(a->fd,"sound_locked: %s\n", conf_get_sound(CONF_SOUND_LOCKED, b_profile.sounds));
1488 ast_cli(a->fd,"sound_unlocked_now: %s\n", conf_get_sound(CONF_SOUND_UNLOCKED_NOW, b_profile.sounds));
1489 ast_cli(a->fd,"sound_lockednow: %s\n", conf_get_sound(CONF_SOUND_LOCKED_NOW, b_profile.sounds));
1490 ast_cli(a->fd,"sound_error_menu: %s\n", conf_get_sound(CONF_SOUND_ERROR_MENU, b_profile.sounds));
1491 ast_cli(a->fd,"sound_join: %s\n", conf_get_sound(CONF_SOUND_JOIN, b_profile.sounds));
1492 ast_cli(a->fd,"sound_leave: %s\n", conf_get_sound(CONF_SOUND_LEAVE, b_profile.sounds));
1493 ast_cli(a->fd,"sound_participants_muted: %s\n", conf_get_sound(CONF_SOUND_PARTICIPANTS_MUTED, b_profile.sounds));
1494 ast_cli(a->fd,"sound_participants_unmuted: %s\n", conf_get_sound(CONF_SOUND_PARTICIPANTS_UNMUTED, b_profile.sounds));
1495 ast_cli(a->fd,"\n");
1497 conf_bridge_profile_destroy(&b_profile);
1501 static char *complete_menu_name(const char *line, const char *word, int pos, int state)
1505 int wordlen = strlen(word);
1506 struct ao2_iterator i;
1507 struct conf_menu *menu = NULL;
1508 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
1514 i = ao2_iterator_init(cfg->menus, 0);
1515 while ((menu = ao2_iterator_next(&i))) {
1516 if (!strncasecmp(menu->name, word, wordlen) && ++which > state) {
1517 res = ast_strdup(menu->name);
1523 ao2_iterator_destroy(&i);
1528 static char *handle_cli_confbridge_show_menus(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1530 struct ao2_iterator it;
1531 struct conf_menu *menu;
1532 RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
1536 e->command = "confbridge show menus";
1538 "Usage confbridge show profile menus\n";
1544 if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
1548 ast_cli(a->fd,"--------- Menus -----------\n");
1549 ao2_lock(cfg->menus);
1550 it = ao2_iterator_init(cfg->menus, 0);
1551 while ((menu = ao2_iterator_next(&it))) {
1552 ast_cli(a->fd,"%s\n", menu->name);
1555 ao2_iterator_destroy(&it);
1556 ao2_unlock(cfg->menus);
1561 static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1563 RAII_VAR(struct conf_menu *, menu, NULL, ao2_cleanup);
1564 RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
1565 struct conf_menu_entry *menu_entry = NULL;
1566 struct conf_menu_action *menu_action = NULL;
1570 e->command = "confbridge show menu";
1572 "Usage confbridge show menu [<menu name>]\n";
1576 return complete_menu_name(a->line, a->word, a->pos, a->n);
1582 return CLI_SHOWUSAGE;
1585 if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
1589 if (!(menu = menu_find(cfg->menus, a->argv[3]))) {
1590 ast_cli(a->fd, "No conference menu named '%s' found!\n", a->argv[3]);
1595 ast_cli(a->fd,"Name: %s\n", menu->name);
1596 AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
1598 ast_cli(a->fd, "%s=", menu_entry->dtmf);
1599 AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
1601 ast_cli(a->fd, ", ");
1603 switch (menu_action->id) {
1604 case MENU_ACTION_TOGGLE_MUTE:
1605 ast_cli(a->fd, "toggle_mute");
1607 case MENU_ACTION_NOOP:
1608 ast_cli(a->fd, "no_op");
1610 case MENU_ACTION_INCREASE_LISTENING:
1611 ast_cli(a->fd, "increase_listening_volume");
1613 case MENU_ACTION_DECREASE_LISTENING:
1614 ast_cli(a->fd, "decrease_listening_volume");
1616 case MENU_ACTION_RESET_LISTENING:
1617 ast_cli(a->fd, "reset_listening_volume");
1619 case MENU_ACTION_RESET_TALKING:
1620 ast_cli(a->fd, "reset_talking_volume");
1622 case MENU_ACTION_INCREASE_TALKING:
1623 ast_cli(a->fd, "increase_talking_volume");
1625 case MENU_ACTION_DECREASE_TALKING:
1626 ast_cli(a->fd, "decrease_talking_volume");
1628 case MENU_ACTION_PLAYBACK:
1629 ast_cli(a->fd, "playback(%s)", menu_action->data.playback_file);
1631 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
1632 ast_cli(a->fd, "playback_and_continue(%s)", menu_action->data.playback_file);
1634 case MENU_ACTION_DIALPLAN_EXEC:
1635 ast_cli(a->fd, "dialplan_exec(%s,%s,%d)",
1636 menu_action->data.dialplan_args.context,
1637 menu_action->data.dialplan_args.exten,
1638 menu_action->data.dialplan_args.priority);
1640 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
1641 ast_cli(a->fd, "admin_toggle_conference_lock");
1643 case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
1644 ast_cli(a->fd, "admin_toggle_mute_participants");
1646 case MENU_ACTION_PARTICIPANT_COUNT:
1647 ast_cli(a->fd, "participant_count");
1649 case MENU_ACTION_ADMIN_KICK_LAST:
1650 ast_cli(a->fd, "admin_kick_last");
1652 case MENU_ACTION_LEAVE:
1653 ast_cli(a->fd, "leave_conference");
1655 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
1656 ast_cli(a->fd, "set_as_single_video_src");
1658 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
1659 ast_cli(a->fd, "release_as_single_video_src");
1664 ast_cli(a->fd,"\n");
1672 static struct ast_cli_entry cli_confbridge_parser[] = {
1673 AST_CLI_DEFINE(handle_cli_confbridge_show_user_profile, "Show a conference user profile."),
1674 AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profile, "Show a conference bridge profile."),
1675 AST_CLI_DEFINE(handle_cli_confbridge_show_menu, "Show a conference menu"),
1676 AST_CLI_DEFINE(handle_cli_confbridge_show_user_profiles, "Show a list of conference user profiles."),
1677 AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profiles, "Show a list of conference bridge profiles."),
1678 AST_CLI_DEFINE(handle_cli_confbridge_show_menus, "Show a list of conference menus"),
1682 static void confbridge_cfg_destructor(void *obj)
1684 struct confbridge_cfg *cfg = obj;
1685 ao2_cleanup(cfg->user_profiles);
1686 ao2_cleanup(cfg->bridge_profiles);
1687 ao2_cleanup(cfg->menus);
1690 void *confbridge_cfg_alloc(void)
1692 struct confbridge_cfg *cfg;
1694 if (!(cfg = ao2_alloc(sizeof(*cfg), confbridge_cfg_destructor))) {
1698 if (!(cfg->user_profiles = ao2_container_alloc(283, user_hash_cb, user_cmp_cb))) {
1702 if (!(cfg->bridge_profiles = ao2_container_alloc(283, bridge_hash_cb, bridge_cmp_cb))) {
1706 if (!(cfg->menus = ao2_container_alloc(283, menu_hash_cb, menu_cmp_cb))) {
1716 static int announce_user_count_all_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1718 struct user_profile *u_profile = obj;
1720 if (strcasecmp(var->name, "announce_user_count_all")) {
1723 if (ast_true(var->value)) {
1724 u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL;
1725 } else if (ast_false(var->value)) {
1726 u_profile->flags = u_profile->flags & ~USER_OPT_ANNOUNCEUSERCOUNTALL;
1727 } else if (sscanf(var->value, "%30u", &u_profile->announce_user_count_all_after) == 1) {
1728 u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL;
1735 static int mix_interval_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1737 struct bridge_profile *b_profile = obj;
1739 if (strcasecmp(var->name, "mixing_interval")) {
1742 if (sscanf(var->value, "%30u", &b_profile->mix_interval) != 1) {
1745 switch (b_profile->mix_interval) {
1756 static int video_mode_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1758 struct bridge_profile *b_profile = obj;
1760 if (strcasecmp(var->name, "video_mode")) {
1763 if (!strcasecmp(var->value, "first_marked")) {
1764 ast_set_flags_to(b_profile,
1765 BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
1766 | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
1767 | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER,
1768 BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED);
1769 } else if (!strcasecmp(var->value, "last_marked")) {
1770 ast_set_flags_to(b_profile,
1771 BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
1772 | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
1773 | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER,
1774 BRIDGE_OPT_VIDEO_SRC_LAST_MARKED);
1775 } else if (!strcasecmp(var->value, "follow_talker")) {
1776 ast_set_flags_to(b_profile,
1777 BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
1778 | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
1779 | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER,
1780 BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
1781 } else if (!strcasecmp(var->value, "none")) {
1782 ast_clear_flag(b_profile,
1783 BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
1784 | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
1785 | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
1792 static int user_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1794 struct user_profile *u_profile = obj;
1796 return conf_find_user_profile(NULL, var->value, u_profile) ? 0 : -1;
1799 static int bridge_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1801 struct bridge_profile *b_profile = obj;
1802 struct bridge_profile_sounds *sounds = bridge_profile_sounds_alloc();
1803 struct bridge_profile_sounds *oldsounds = b_profile->sounds;
1808 if (!(conf_find_bridge_profile(NULL, var->value, b_profile))) {
1809 ao2_ref(sounds, -1);
1812 /* Using a bridge profile as a template is a little complicated due to the sounds. Since the sounds
1813 * structure of a dynamic profile will need to be altered, a completely new sounds structure must be
1814 * created instead of simply holding a reference to the one built by the config file. */
1815 ast_string_field_set(sounds, onlyperson, b_profile->sounds->onlyperson);
1816 ast_string_field_set(sounds, onlyone, b_profile->sounds->onlyone);
1817 ast_string_field_set(sounds, hasjoin, b_profile->sounds->hasjoin);
1818 ast_string_field_set(sounds, hasleft, b_profile->sounds->hasleft);
1819 ast_string_field_set(sounds, kicked, b_profile->sounds->kicked);
1820 ast_string_field_set(sounds, muted, b_profile->sounds->muted);
1821 ast_string_field_set(sounds, unmuted, b_profile->sounds->unmuted);
1822 ast_string_field_set(sounds, thereare, b_profile->sounds->thereare);
1823 ast_string_field_set(sounds, otherinparty, b_profile->sounds->otherinparty);
1824 ast_string_field_set(sounds, placeintoconf, b_profile->sounds->placeintoconf);
1825 ast_string_field_set(sounds, waitforleader, b_profile->sounds->waitforleader);
1826 ast_string_field_set(sounds, leaderhasleft, b_profile->sounds->leaderhasleft);
1827 ast_string_field_set(sounds, getpin, b_profile->sounds->getpin);
1828 ast_string_field_set(sounds, invalidpin, b_profile->sounds->invalidpin);
1829 ast_string_field_set(sounds, locked, b_profile->sounds->locked);
1830 ast_string_field_set(sounds, unlockednow, b_profile->sounds->unlockednow);
1831 ast_string_field_set(sounds, lockednow, b_profile->sounds->lockednow);
1832 ast_string_field_set(sounds, errormenu, b_profile->sounds->errormenu);
1833 ast_string_field_set(sounds, join, b_profile->sounds->join);
1834 ast_string_field_set(sounds, leave, b_profile->sounds->leave);
1835 ast_string_field_set(sounds, participantsmuted, b_profile->sounds->participantsmuted);
1836 ast_string_field_set(sounds, participantsunmuted, b_profile->sounds->participantsunmuted);
1838 ao2_ref(b_profile->sounds, -1); /* sounds struct copied over to it from the template by reference only. */
1839 ao2_ref(oldsounds, -1); /* original sounds struct we don't need anymore */
1840 b_profile->sounds = sounds; /* the new sounds struct that is a deep copy of the one from the template. */
1845 static int sound_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1847 set_sound(var->name, var->value, obj);
1851 static int menu_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1853 add_menu_entry(obj, var->name, var->value);
1857 static int verify_default_profiles(void)
1859 RAII_VAR(struct user_profile *, user_profile, NULL, ao2_cleanup);
1860 RAII_VAR(struct bridge_profile *, bridge_profile, NULL, ao2_cleanup);
1861 struct confbridge_cfg *cfg = aco_pending_config(&cfg_info);
1867 bridge_profile = ao2_find(cfg->bridge_profiles, DEFAULT_BRIDGE_PROFILE, OBJ_KEY);
1868 if (!bridge_profile) {
1869 bridge_profile = bridge_profile_alloc(DEFAULT_BRIDGE_PROFILE);
1870 if (!bridge_profile) {
1873 ast_log(AST_LOG_NOTICE, "Adding %s profile to app_confbridge\n", DEFAULT_BRIDGE_PROFILE);
1874 aco_set_defaults(&bridge_type, DEFAULT_BRIDGE_PROFILE, bridge_profile);
1875 ao2_link(cfg->bridge_profiles, bridge_profile);
1878 user_profile = ao2_find(cfg->user_profiles, DEFAULT_USER_PROFILE, OBJ_KEY);
1879 if (!user_profile) {
1880 user_profile = user_profile_alloc(DEFAULT_USER_PROFILE);
1881 if (!user_profile) {
1884 ast_log(AST_LOG_NOTICE, "Adding %s profile to app_confbridge\n", DEFAULT_USER_PROFILE);
1885 aco_set_defaults(&user_type, DEFAULT_USER_PROFILE, user_profile);
1886 ao2_link(cfg->user_profiles, user_profile);
1892 int conf_load_config(void)
1894 if (aco_info_init(&cfg_info)) {
1899 aco_option_register(&cfg_info, "type", ACO_EXACT, user_types, NULL, OPT_NOOP_T, 0, 0);
1900 aco_option_register(&cfg_info, "admin", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ADMIN);
1901 aco_option_register(&cfg_info, "marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_MARKEDUSER);
1902 aco_option_register(&cfg_info, "startmuted", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_STARTMUTED);
1903 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);
1904 aco_option_register(&cfg_info, "quiet", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_QUIET);
1905 aco_option_register_custom(&cfg_info, "announce_user_count_all", ACO_EXACT, user_types, "no", announce_user_count_all_handler, 0);
1906 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);
1907 /* Negative logic. Defaults to "yes" and evaluates with ast_false(). If !ast_false(), USER_OPT_NOONLYPERSON is cleared */
1908 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);
1909 aco_option_register(&cfg_info, "wait_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_WAITMARKED);
1910 aco_option_register(&cfg_info, "end_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKED);
1911 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);
1912 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);
1913 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);
1914 aco_option_register(&cfg_info, "pin", ACO_EXACT, user_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct user_profile, pin));
1915 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));
1916 aco_option_register(&cfg_info, "announcement", ACO_EXACT, user_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct user_profile, announcement));
1917 aco_option_register(&cfg_info, "denoise", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_DENOISE);
1918 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);
1919 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));
1920 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));
1921 aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_JITTERBUFFER);
1922 /* This option should only be used with the CONFBRIDGE dialplan function */
1923 aco_option_register_custom(&cfg_info, "template", ACO_EXACT, user_types, NULL, user_template_handler, 0);
1925 /* XXX ASTERISK-21271 need a user supplied bridge merge_priority to merge ConfBridges (default = 1, range 1-INT_MAX) */
1926 /* Bridge options */
1927 aco_option_register(&cfg_info, "type", ACO_EXACT, bridge_types, NULL, OPT_NOOP_T, 0, 0);
1928 aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), USER_OPT_JITTERBUFFER);
1929 /* "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 */
1930 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);
1931 aco_option_register_custom(&cfg_info, "mixing_interval", ACO_EXACT, bridge_types, "20", mix_interval_handler, 0);
1932 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);
1933 aco_option_register_custom(&cfg_info, "video_mode", ACO_EXACT, bridge_types, NULL, video_mode_handler, 0);
1934 aco_option_register(&cfg_info, "record_file_append", ACO_EXACT, bridge_types, "yes", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), BRIDGE_OPT_RECORD_FILE_APPEND);
1935 aco_option_register(&cfg_info, "max_members", ACO_EXACT, bridge_types, "0", OPT_UINT_T, 0, FLDSET(struct bridge_profile, max_members));
1936 aco_option_register(&cfg_info, "record_file", ACO_EXACT, bridge_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, rec_file));
1937 aco_option_register_custom(&cfg_info, "^sound_", ACO_REGEX, bridge_types, NULL, sound_option_handler, 0);
1938 /* This option should only be used with the CONFBRIDGE dialplan function */
1939 aco_option_register_custom(&cfg_info, "template", ACO_EXACT, bridge_types, NULL, bridge_template_handler, 0);
1942 aco_option_register(&cfg_info, "type", ACO_EXACT, menu_types, NULL, OPT_NOOP_T, 0, 0);
1943 aco_option_register_custom(&cfg_info, "^[0-9A-D*#]+$", ACO_REGEX, menu_types, NULL, menu_option_handler, 0);
1945 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1949 if (ast_cli_register_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser))) {
1955 conf_destroy_config();
1959 int conf_reload_config(void)
1961 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
1962 /* On a reload, just keep the config we already have in place. */
1968 static void conf_user_profile_copy(struct user_profile *dst, struct user_profile *src)
1973 const struct user_profile *conf_find_user_profile(struct ast_channel *chan, const char *user_profile_name, struct user_profile *result)
1975 struct user_profile *tmp2;
1976 struct ast_datastore *datastore = NULL;
1977 struct func_confbridge_data *b_data = NULL;
1978 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
1985 ast_channel_lock(chan);
1986 datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
1987 ast_channel_unlock(chan);
1989 b_data = datastore->data;
1990 if (b_data->u_usable) {
1991 conf_user_profile_copy(result, &b_data->u_profile);
1997 if (ast_strlen_zero(user_profile_name)) {
1998 user_profile_name = DEFAULT_USER_PROFILE;
2000 if (!(tmp2 = ao2_find(cfg->user_profiles, user_profile_name, OBJ_KEY))) {
2004 conf_user_profile_copy(result, tmp2);
2011 void conf_bridge_profile_copy(struct bridge_profile *dst, struct bridge_profile *src)
2015 ao2_ref(src->sounds, +1);
2019 void conf_bridge_profile_destroy(struct bridge_profile *b_profile)
2021 if (b_profile->sounds) {
2022 ao2_ref(b_profile->sounds, -1);
2023 b_profile->sounds = NULL;
2027 const struct bridge_profile *conf_find_bridge_profile(struct ast_channel *chan, const char *bridge_profile_name, struct bridge_profile *result)
2029 struct bridge_profile *tmp2;
2030 struct ast_datastore *datastore = NULL;
2031 struct func_confbridge_data *b_data = NULL;
2032 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
2039 ast_channel_lock(chan);
2040 datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
2041 ast_channel_unlock(chan);
2043 b_data = datastore->data;
2044 if (b_data->b_usable) {
2045 conf_bridge_profile_copy(result, &b_data->b_profile);
2050 if (ast_strlen_zero(bridge_profile_name)) {
2051 bridge_profile_name = DEFAULT_BRIDGE_PROFILE;
2053 if (!(tmp2 = ao2_find(cfg->bridge_profiles, bridge_profile_name, OBJ_KEY))) {
2057 conf_bridge_profile_copy(result, tmp2);
2064 struct dtmf_menu_hook_pvt {
2065 struct confbridge_user *user;
2066 struct conf_menu_entry menu_entry;
2067 struct conf_menu *menu;
2070 static void menu_hook_destroy(void *hook_pvt)
2072 struct dtmf_menu_hook_pvt *pvt = hook_pvt;
2073 struct conf_menu_action *action = NULL;
2075 ao2_ref(pvt->menu, -1);
2077 while ((action = AST_LIST_REMOVE_HEAD(&pvt->menu_entry.actions, action))) {
2083 static int menu_hook_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
2085 struct dtmf_menu_hook_pvt *pvt = hook_pvt;
2087 return conf_handle_dtmf(bridge_channel, pvt->user, &pvt->menu_entry, pvt->menu);
2090 static int copy_menu_entry(struct conf_menu_entry *dst, struct conf_menu_entry *src)
2092 struct conf_menu_action *menu_action = NULL;
2093 struct conf_menu_action *new_menu_action = NULL;
2095 memcpy(dst, src, sizeof(*dst));
2096 AST_LIST_HEAD_INIT_NOLOCK(&dst->actions);
2098 AST_LIST_TRAVERSE(&src->actions, menu_action, action) {
2099 if (!(new_menu_action = ast_calloc(1, sizeof(*new_menu_action)))) {
2102 memcpy(new_menu_action, menu_action, sizeof(*new_menu_action));
2103 AST_LIST_INSERT_HEAD(&dst->actions, new_menu_action, action);
2108 void conf_menu_entry_destroy(struct conf_menu_entry *menu_entry)
2110 struct conf_menu_action *menu_action = NULL;
2111 while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
2112 ast_free(menu_action);
2116 int conf_find_menu_entry_by_sequence(const char *dtmf_sequence, struct conf_menu *menu, struct conf_menu_entry *result)
2118 struct conf_menu_entry *menu_entry = NULL;
2121 AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
2122 if (!strcasecmp(menu_entry->dtmf, dtmf_sequence)) {
2123 copy_menu_entry(result, menu_entry);
2133 int conf_set_menu_to_user(const char *menu_name, struct confbridge_user *user)
2135 struct conf_menu *menu;
2136 struct conf_menu_entry *menu_entry = NULL;
2137 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
2143 if (!(menu = menu_find(cfg->menus, menu_name))) {
2147 AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
2148 struct dtmf_menu_hook_pvt *pvt;
2150 if (!(pvt = ast_calloc(1, sizeof(*pvt)))) {
2155 if (copy_menu_entry(&pvt->menu_entry, menu_entry)) {
2165 if (ast_bridge_dtmf_hook(&user->features, pvt->menu_entry.dtmf,
2166 menu_hook_callback, pvt, menu_hook_destroy, 0)) {
2167 menu_hook_destroy(pvt);
2177 void conf_destroy_config(void)
2179 ast_cli_unregister_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser));
2180 aco_info_destroy(&cfg_info);
2181 ao2_global_obj_release(cfg_handle);