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="language" default="en">
261 <synopsis>The language used for announcements to the conference.</synopsis>
263 By default, announcements to a conference use English. Which means
264 the prompts played to all users within the conference will be
265 English. By changing the language of a bridge, this will change
266 the language of the prompts played to all users.
267 </para></description>
269 <configOption name="mixing_interval">
270 <synopsis>Sets the internal mixing interval in milliseconds for the bridge</synopsis>
272 Sets the internal mixing interval in milliseconds for the bridge. This
273 number reflects how tight or loose the mixing will be for the conference.
274 In order to improve performance a larger mixing interval such as 40ms may
275 be chosen. Using a larger mixing interval comes at the cost of introducing
276 larger amounts of delay into the bridge. Valid values here are 10, 20, 40,
278 </para></description>
280 <configOption name="record_conference">
281 <synopsis>Record the conference starting with the first active user's entrance and ending with the last active user's exit</synopsis>
283 Records the conference call starting when the first user
284 enters the room, and ending when the last user exits the room.
285 The default recorded filename is
286 <filename>'confbridge-${name of conference bridge}-${start time}.wav'</filename>
287 and the default format is 8khz slinear. This file will be
288 located in the configured monitoring directory in <filename>asterisk.conf</filename>.
289 </para></description>
291 <configOption name="record_file" default="confbridge-${name of conference bridge}-${start time}.wav">
292 <synopsis>The filename of the conference recording</synopsis>
294 When <replaceable>record_conference</replaceable> is set to yes, the specific name of the
295 record file can be set using this option. Note that since multiple
296 conferences may use the same bridge profile, this may cause issues
297 depending on the configuration. It is recommended to only use this
298 option dynamically with the <literal>CONFBRIDGE()</literal> dialplan function. This
299 allows the record name to be specified and a unique name to be chosen.
300 By default, the record_file is stored in Asterisk's spool/monitor directory
301 with a unique filename starting with the 'confbridge' prefix.
302 </para></description>
304 <configOption name="record_file_append" default="yes">
305 <synopsis>Append record file when starting/stopping on same conference recording</synopsis>
307 When <replaceable>record_file_append</replaceable> is set to yes, stopping and starting recording on a
308 conference adds the new portion to end of current record_file. When this is
309 set to no, a new <replaceable>record_file</replaceable> is generated every time you start then stop recording
311 </para></description>
313 <configOption name="video_mode">
314 <synopsis>Sets how confbridge handles video distribution to the conference participants</synopsis>
316 Sets how confbridge handles video distribution to the conference participants.
317 Note that participants wanting to view and be the source of a video feed
318 <emphasis>MUST</emphasis> be sharing the same video codec. Also, using video in conjunction with
319 with the jitterbuffer currently results in the audio being slightly out of sync
320 with the video. This is a result of the jitterbuffer only working on the audio
321 stream. It is recommended to disable the jitterbuffer when video is used.</para>
324 <para>No video sources are set by default in the conference. It is still
325 possible for a user to be set as a video source via AMI or DTMF action
328 <enum name="follow_talker">
329 <para>The video feed will follow whoever is talking and providing video.</para>
331 <enum name="last_marked">
332 <para>The last marked user to join the conference with video capabilities
333 will be the single source of video distributed to all participants.
334 If multiple marked users are capable of video, the last one to join
335 is always the source, when that user leaves it goes to the one who
336 joined before them.</para>
338 <enum name="first_marked">
339 <para>The first marked user to join the conference with video capabilities
340 is the single source of video distribution among all participants. If
341 that user leaves, the marked user to join after them becomes the source.</para>
346 <configOption name="max_members">
347 <synopsis>Limit the maximum number of participants for a single conference</synopsis>
349 This option limits the number of participants for a single
350 conference to a specific number. By default conferences
351 have no participant limit. After the limit is reached, the
352 conference will be locked until someone leaves. Note however
353 that an Admin user will always be alowed to join the conference
354 regardless if this limit is reached or not.
355 </para></description>
357 <configOption name="^sound_">
358 <synopsis>Override the various conference bridge sound files</synopsis>
360 All sounds in the conference are customizable using the bridge profile options below.
361 Simply state the option followed by the filename or full path of the filename after
362 the option. Example: <literal>sound_had_joined=conf-hasjoin</literal> This will play the <literal>conf-hasjoin</literal>
363 sound file found in the sounds directory when announcing someone's name is joining the
366 <enum name="sound_join"><para>The sound played to everyone when someone enters the conference.</para></enum>
367 <enum name="sound_leave"><para>The sound played to everyone when someone leaves the conference.</para></enum>
368 <enum name="sound_has_joined"><para>The sound played before announcing someone's name has
369 joined the conference. This is used for user intros.
370 Example <literal>"_____ has joined the conference"</literal></para></enum>
371 <enum name="sound_has_left"><para>The sound played when announcing someone's name has
372 left the conference. This is used for user intros.
373 Example <literal>"_____ has left the conference"</literal></para></enum>
374 <enum name="sound_kicked"><para>The sound played to a user who has been kicked from the conference.</para></enum>
375 <enum name="sound_muted"><para>The sound played when the mute option it toggled on.</para></enum>
376 <enum name="sound_unmuted"><para>The sound played when the mute option it toggled off.</para></enum>
377 <enum name="sound_only_person"><para>The sound played when the user is the only person in the conference.</para></enum>
378 <enum name="sound_only_one"><para>The sound played to a user when there is only one other
379 person is in the conference.</para></enum>
380 <enum name="sound_there_are"><para>The sound played when announcing how many users there
381 are in a conference.</para></enum>
382 <enum name="sound_other_in_party"><para>This file is used in conjunction with <literal>sound_there_are</literal>
383 when announcing how many users there are in the conference.
384 The sounds are stringed together like this.
385 <literal>"sound_there_are" ${number of participants} "sound_other_in_party"</literal></para></enum>
386 <enum name="sound_place_into_conference"><para>The sound played when someone is placed into the conference
387 after waiting for a marked user.</para></enum>
388 <enum name="sound_wait_for_leader"><para>The sound played when a user is placed into a conference that
389 can not start until a marked user enters.</para></enum>
390 <enum name="sound_leader_has_left"><para>The sound played when the last marked user leaves the conference.</para></enum>
391 <enum name="sound_get_pin"><para>The sound played when prompting for a conference pin number.</para></enum>
392 <enum name="sound_invalid_pin"><para>The sound played when an invalid pin is entered too many times.</para></enum>
393 <enum name="sound_locked"><para>The sound played to a user trying to join a locked conference.</para></enum>
394 <enum name="sound_locked_now"><para>The sound played to an admin after toggling the conference to locked mode.</para></enum>
395 <enum name="sound_unlocked_now"><para>The sound played to an admin after toggling the conference to unlocked mode.</para></enum>
396 <enum name="sound_error_menu"><para>The sound played when an invalid menu option is entered.</para></enum>
400 <configOption name="template">
401 <synopsis>When using the CONFBRIDGE dialplan function, use a bridge profile as a template for creating a new temporary profile</synopsis>
404 <configObject name="menu">
405 <synopsis>A conference user menu</synopsis>
407 <para>Conference users, as defined by a <replaceable>conf_user</replaceable>,
408 can have a DTMF menu assigned to their profile when they enter the
409 <literal>ConfBridge</literal> application.</para>
411 <configOption name="type">
412 <synopsis>Define this configuration category as a menu</synopsis>
413 <description><para>The type parameter determines how a context in the
414 configuration file is interpreted.</para>
416 <enum name="user"><para>Configure the context as a <replaceable>user_profile</replaceable></para></enum>
417 <enum name="bridge"><para>Configure the context as a <replaceable>bridge_profile</replaceable></para></enum>
418 <enum name="menu"><para>Configure the context as a <replaceable>menu</replaceable></para></enum>
422 <configOption name="^[0-9A-D*#]+$">
423 <synopsis>DTMF sequences to assign various confbridge actions to</synopsis>
425 <para>The ConfBridge application also has the ability to apply custom DTMF menus to
426 each channel using the application. Like the User and Bridge profiles a menu
427 is passed in to ConfBridge as an argument in the dialplan.</para>
428 <para>Below is a list of menu actions that can be assigned to a DTMF sequence.</para>
430 A single DTMF sequence can have multiple actions associated with it. This is
431 accomplished by stringing the actions together and using a <literal>,</literal> as the
432 delimiter. Example: Both listening and talking volume is reset when <literal>5</literal> is
433 pressed. <literal>5=reset_talking_volume, reset_listening_volume</literal></para></note>
435 <enum name="playback(filename&filename2&...)"><para>
436 <literal>playback</literal> will play back an audio file to a channel
437 and then immediately return to the conference.
438 This file can not be interupted by DTMF.
439 Multiple files can be chained together using the
440 <literal>&</literal> character.</para></enum>
441 <enum name="playback_and_continue(filename&filename2&...)"><para>
442 <literal>playback_and_continue</literal> will
443 play back a prompt while continuing to
444 collect the dtmf sequence. This is useful
445 when using a menu prompt that describes all
446 the menu options. Note however that any DTMF
447 during this action will terminate the prompts
448 playback. Prompt files can be chained together
449 using the <literal>&</literal> character as a delimiter.</para></enum>
450 <enum name="toggle_mute"><para>
451 Toggle turning on and off mute. Mute will make the user silent
452 to everyone else, but the user will still be able to listen in.
453 continue to collect the dtmf sequence.</para></enum>
454 <enum name="no_op"><para>
455 This action does nothing (No Operation). Its only real purpose exists for
456 being able to reserve a sequence in the config as a menu exit sequence.</para></enum>
457 <enum name="decrease_listening_volume"><para>
458 Decreases the channel's listening volume.</para></enum>
459 <enum name="increase_listening_volume"><para>
460 Increases the channel's listening volume.</para></enum>
461 <enum name="reset_listening_volume"><para>
462 Reset channel's listening volume to default level.</para></enum>
463 <enum name="decrease_talking_volume"><para>
464 Decreases the channel's talking volume.</para></enum>
465 <enum name="increase_talking_volume"><para>
466 Increases the channel's talking volume.</para></enum>
467 <enum name="reset_talking_volume"><para>
468 Reset channel's talking volume to default level.</para></enum>
469 <enum name="dialplan_exec(context,exten,priority)"><para>
470 The <literal>dialplan_exec</literal> action allows a user
471 to escape from the conference and execute
472 commands in the dialplan. Once the dialplan
473 exits the user will be put back into the
474 conference. The possibilities are endless!</para></enum>
475 <enum name="leave_conference"><para>
476 This action allows a user to exit the conference and continue
477 execution in the dialplan.</para></enum>
478 <enum name="admin_kick_last"><para>
479 This action allows an Admin to kick the last participant from the
480 conference. This action will only work for admins which allows
481 a single menu to be used for both users and admins.</para></enum>
482 <enum name="admin_toggle_conference_lock"><para>
483 This action allows an Admin to toggle locking and
484 unlocking the conference. Non admins can not use
485 this action even if it is in their menu.</para></enum>
486 <enum name="set_as_single_video_src"><para>
487 This action allows any user to set themselves as the
488 single video source distributed to all participants.
489 This will make the video feed stick to them regardless
490 of what the <literal>video_mode</literal> is set to.</para></enum>
491 <enum name="release_as_single_video_src"><para>
492 This action allows a user to release themselves as
493 the video source. If <literal>video_mode</literal> is not set to <literal>none</literal>
494 this action will result in the conference returning to
495 whatever video mode the bridge profile is using.</para>
496 <para>Note that this action will have no effect if the user
497 is not currently the video source. Also, the user is
498 not guaranteed by using this action that they will not
499 become the video source again. The bridge will return
500 to whatever operation the <literal>video_mode</literal> option is set to
501 upon release of the video src.</para></enum>
502 <enum name="admin_toggle_mute_participants"><para>
503 This action allows an administrator to toggle the mute
504 state for all non-admins within a conference. All
505 admin users are unaffected by this option. Note that all
506 users, regardless of their admin status, are notified
507 that the conference is muted.</para></enum>
508 <enum name="participant_count"><para>
509 This action plays back the number of participants currently
510 in a conference</para></enum>
519 struct confbridge_cfg {
520 struct ao2_container *bridge_profiles;
521 struct ao2_container *user_profiles;
522 struct ao2_container *menus;
525 static int verify_default_profiles(void);
526 static void *bridge_profile_alloc(const char *category);
527 static void *bridge_profile_find(struct ao2_container *container, const char *category);
528 static struct bridge_profile_sounds *bridge_profile_sounds_alloc(void);
530 static void bridge_profile_destructor(void *obj)
532 struct bridge_profile *b_profile = obj;
533 ao2_cleanup(b_profile->sounds);
536 static void *bridge_profile_alloc(const char *category)
538 struct bridge_profile *b_profile;
540 if (!(b_profile = ao2_alloc(sizeof(*b_profile), bridge_profile_destructor))) {
544 if (!(b_profile->sounds = bridge_profile_sounds_alloc())) {
545 ao2_ref(b_profile, -1);
549 ast_copy_string(b_profile->name, category, sizeof(b_profile->name));
554 static void *bridge_profile_find(struct ao2_container *container, const char *category)
556 return ao2_find(container, category, OBJ_KEY);
559 static struct aco_type bridge_type = {
561 .name = "bridge_profile",
562 .category_match = ACO_BLACKLIST,
563 .category = "^general$",
564 .matchfield = "type",
565 .matchvalue = "bridge",
566 .item_alloc = bridge_profile_alloc,
567 .item_find = bridge_profile_find,
568 .item_offset = offsetof(struct confbridge_cfg, bridge_profiles),
571 static void *user_profile_alloc(const char *category);
572 static void *user_profile_find(struct ao2_container *container, const char *category);
573 static void user_profile_destructor(void *obj)
578 static void *user_profile_alloc(const char *category)
580 struct user_profile *u_profile;
582 if (!(u_profile = ao2_alloc(sizeof(*u_profile), user_profile_destructor))) {
586 ast_copy_string(u_profile->name, category, sizeof(u_profile->name));
591 static void *user_profile_find(struct ao2_container *container, const char *category)
593 return ao2_find(container, category, OBJ_KEY);
596 static struct aco_type user_type = {
598 .name = "user_profile",
599 .category_match = ACO_BLACKLIST,
600 .category = "^general$",
601 .matchfield = "type",
602 .matchvalue = "user",
603 .item_alloc = user_profile_alloc,
604 .item_find = user_profile_find,
605 .item_offset = offsetof(struct confbridge_cfg, user_profiles),
608 static void *menu_alloc(const char *category);
609 static void *menu_find(struct ao2_container *container, const char *category);
610 static void menu_destructor(void *obj);
612 static void *menu_alloc(const char *category)
614 struct conf_menu *menu;
615 if (!(menu = ao2_alloc(sizeof(*menu), menu_destructor))) {
618 ast_copy_string(menu->name, category, sizeof(menu->name));
622 static void *menu_find(struct ao2_container *container, const char *category)
624 return ao2_find(container, category, OBJ_KEY);
627 static struct aco_type menu_type = {
630 .category_match = ACO_BLACKLIST,
631 .category = "^general$",
632 .matchfield = "type",
633 .matchvalue = "menu",
634 .item_alloc = menu_alloc,
635 .item_find = menu_find,
636 .item_offset = offsetof(struct confbridge_cfg, menus),
639 /* Used to pass to aco_option_register */
640 static struct aco_type *bridge_types[] = ACO_TYPES(&bridge_type);
641 static struct aco_type *menu_types[] = ACO_TYPES(&menu_type);
642 static struct aco_type *user_types[] = ACO_TYPES(&user_type);
644 /* The general category is reserved, but unused */
645 static struct aco_type general_type = {
648 .category_match = ACO_WHITELIST,
649 .category = "^general$",
652 static struct aco_file confbridge_conf = {
653 .filename = "confbridge.conf",
654 .types = ACO_TYPES(&bridge_type, &user_type, &menu_type, &general_type),
657 static AO2_GLOBAL_OBJ_STATIC(cfg_handle);
659 static void *confbridge_cfg_alloc(void);
661 CONFIG_INFO_STANDARD(cfg_info, cfg_handle, confbridge_cfg_alloc,
662 .files = ACO_FILES(&confbridge_conf),
663 .pre_apply_config = verify_default_profiles,
666 /*! bridge profile container functions */
667 static int bridge_cmp_cb(void *obj, void *arg, int flags)
669 const struct bridge_profile *left = obj;
670 const struct bridge_profile *right = arg;
671 const char *right_name = arg;
674 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
677 right_name = right->name;
680 cmp = strcasecmp(left->name, right_name);
682 case OBJ_PARTIAL_KEY:
683 cmp = strncasecmp(left->name, right_name, strlen(right_name));
686 return cmp ? 0 : CMP_MATCH;
689 static int bridge_hash_cb(const void *obj, const int flags)
691 const struct bridge_profile *b_profile = obj;
692 const char *name = obj;
695 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
698 name = b_profile->name;
701 hash = ast_str_case_hash(name);
703 case OBJ_PARTIAL_KEY:
704 /* Should never happen in hash callback. */
712 /*! menu container functions */
713 static int menu_cmp_cb(void *obj, void *arg, int flags)
715 const struct conf_menu *left = obj;
716 const struct conf_menu *right = arg;
717 const char *right_name = arg;
720 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
723 right_name = right->name;
726 cmp = strcasecmp(left->name, right_name);
728 case OBJ_PARTIAL_KEY:
729 cmp = strncasecmp(left->name, right_name, strlen(right_name));
732 return cmp ? 0 : CMP_MATCH;
735 static int menu_hash_cb(const void *obj, const int flags)
737 const struct conf_menu *menu = obj;
738 const char *name = obj;
741 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
747 hash = ast_str_case_hash(name);
749 case OBJ_PARTIAL_KEY:
750 /* Should never happen in hash callback. */
758 static void menu_destructor(void *obj)
760 struct conf_menu *menu = obj;
761 struct conf_menu_entry *entry = NULL;
763 while ((entry = AST_LIST_REMOVE_HEAD(&menu->entries, entry))) {
764 conf_menu_entry_destroy(entry);
769 /*! User profile container functions */
770 static int user_cmp_cb(void *obj, void *arg, int flags)
772 const struct user_profile *left = obj;
773 const struct user_profile *right = arg;
774 const char *right_name = arg;
777 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
780 right_name = right->name;
783 cmp = strcasecmp(left->name, right_name);
785 case OBJ_PARTIAL_KEY:
786 cmp = strncasecmp(left->name, right_name, strlen(right_name));
789 return cmp ? 0 : CMP_MATCH;
792 static int user_hash_cb(const void *obj, const int flags)
794 const struct user_profile *u_profile = obj;
795 const char *name = obj;
798 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
801 name = u_profile->name;
804 hash = ast_str_case_hash(name);
806 case OBJ_PARTIAL_KEY:
807 /* Should never happen in hash callback. */
815 /*! Bridge Profile Sounds functions */
816 static void bridge_profile_sounds_destroy_cb(void *obj)
818 struct bridge_profile_sounds *sounds = obj;
819 ast_string_field_free_memory(sounds);
822 static struct bridge_profile_sounds *bridge_profile_sounds_alloc(void)
824 struct bridge_profile_sounds *sounds = ao2_alloc(sizeof(*sounds), bridge_profile_sounds_destroy_cb);
829 if (ast_string_field_init(sounds, 512)) {
837 static int set_sound(const char *sound_name, const char *sound_file, struct bridge_profile *b_profile)
839 struct bridge_profile_sounds *sounds = b_profile->sounds;
840 if (ast_strlen_zero(sound_file)) {
844 if (!strcasecmp(sound_name, "sound_only_person")) {
845 ast_string_field_set(sounds, onlyperson, sound_file);
846 } else if (!strcasecmp(sound_name, "sound_only_one")) {
847 ast_string_field_set(sounds, onlyone, sound_file);
848 } else if (!strcasecmp(sound_name, "sound_has_joined")) {
849 ast_string_field_set(sounds, hasjoin, sound_file);
850 } else if (!strcasecmp(sound_name, "sound_has_left")) {
851 ast_string_field_set(sounds, hasleft, sound_file);
852 } else if (!strcasecmp(sound_name, "sound_kicked")) {
853 ast_string_field_set(sounds, kicked, sound_file);
854 } else if (!strcasecmp(sound_name, "sound_muted")) {
855 ast_string_field_set(sounds, muted, sound_file);
856 } else if (!strcasecmp(sound_name, "sound_unmuted")) {
857 ast_string_field_set(sounds, unmuted, sound_file);
858 } else if (!strcasecmp(sound_name, "sound_there_are")) {
859 ast_string_field_set(sounds, thereare, sound_file);
860 } else if (!strcasecmp(sound_name, "sound_other_in_party")) {
861 ast_string_field_set(sounds, otherinparty, sound_file);
862 } else if (!strcasecmp(sound_name, "sound_place_into_conference")) {
863 ast_string_field_set(sounds, placeintoconf, sound_file);
864 } else if (!strcasecmp(sound_name, "sound_wait_for_leader")) {
865 ast_string_field_set(sounds, waitforleader, sound_file);
866 } else if (!strcasecmp(sound_name, "sound_leader_has_left")) {
867 ast_string_field_set(sounds, leaderhasleft, sound_file);
868 } else if (!strcasecmp(sound_name, "sound_get_pin")) {
869 ast_string_field_set(sounds, getpin, sound_file);
870 } else if (!strcasecmp(sound_name, "sound_invalid_pin")) {
871 ast_string_field_set(sounds, invalidpin, sound_file);
872 } else if (!strcasecmp(sound_name, "sound_locked")) {
873 ast_string_field_set(sounds, locked, sound_file);
874 } else if (!strcasecmp(sound_name, "sound_unlocked_now")) {
875 ast_string_field_set(sounds, unlockednow, sound_file);
876 } else if (!strcasecmp(sound_name, "sound_locked_now")) {
877 ast_string_field_set(sounds, lockednow, sound_file);
878 } else if (!strcasecmp(sound_name, "sound_error_menu")) {
879 ast_string_field_set(sounds, errormenu, sound_file);
880 } else if (!strcasecmp(sound_name, "sound_join")) {
881 ast_string_field_set(sounds, join, sound_file);
882 } else if (!strcasecmp(sound_name, "sound_leave")) {
883 ast_string_field_set(sounds, leave, sound_file);
884 } else if (!strcasecmp(sound_name, "sound_participants_muted")) {
885 ast_string_field_set(sounds, participantsmuted, sound_file);
886 } else if (!strcasecmp(sound_name, "sound_participants_unmuted")) {
887 ast_string_field_set(sounds, participantsunmuted, sound_file);
895 /*! CONFBRIDGE dialplan function functions and channel datastore. */
896 struct func_confbridge_data {
897 struct bridge_profile b_profile;
898 struct user_profile u_profile;
899 unsigned int b_usable:1; /*!< Tells if bridge profile is usable or not */
900 unsigned int u_usable:1; /*!< Tells if user profile is usable or not */
902 static void func_confbridge_destroy_cb(void *data)
904 struct func_confbridge_data *b_data = data;
905 conf_bridge_profile_destroy(&b_data->b_profile);
908 static const struct ast_datastore_info confbridge_datastore = {
909 .type = "confbridge",
910 .destroy = func_confbridge_destroy_cb
912 int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value)
914 struct ast_datastore *datastore;
915 struct func_confbridge_data *b_data;
917 struct ast_variable tmpvar = { 0, };
918 AST_DECLARE_APP_ARGS(args,
923 /* parse all the required arguments and make sure they exist. */
924 if (ast_strlen_zero(data)) {
927 parse = ast_strdupa(data);
928 AST_STANDARD_APP_ARGS(args, parse);
929 if (ast_strlen_zero(args.type) || ast_strlen_zero(args.option)) {
933 ast_channel_lock(chan);
934 datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
936 datastore = ast_datastore_alloc(&confbridge_datastore, NULL);
938 ast_channel_unlock(chan);
941 b_data = ast_calloc(1, sizeof(*b_data));
943 ast_channel_unlock(chan);
944 ast_datastore_free(datastore);
947 b_data->b_profile.sounds = bridge_profile_sounds_alloc();
948 if (!b_data->b_profile.sounds) {
949 ast_channel_unlock(chan);
950 ast_datastore_free(datastore);
954 datastore->data = b_data;
955 ast_channel_datastore_add(chan, datastore);
957 b_data = datastore->data;
959 ast_channel_unlock(chan);
961 /* SET(CONFBRIDGE(type,option)=value) */
965 tmpvar.name = args.option;
966 tmpvar.value = value;
967 tmpvar.file = "CONFBRIDGE";
968 if (!strcasecmp(args.type, "bridge")) {
969 if (!aco_process_var(&bridge_type, "dialplan", &tmpvar, &b_data->b_profile)) {
970 b_data->b_usable = 1;
973 } else if (!strcasecmp(args.type, "user")) {
974 if (!aco_process_var(&user_type, "dialplan", &tmpvar, &b_data->u_profile)) {
975 b_data->u_usable = 1;
980 ast_log(LOG_WARNING, "%s(%s,%s) cannot be set to '%s'. Invalid type, option, or value.\n",
981 cmd, args.type, args.option, value);
985 static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum conf_menu_action_id id, char *databuf)
987 struct conf_menu_action *menu_action = ast_calloc(1, sizeof(*menu_action));
992 menu_action->id = id;
995 case MENU_ACTION_NOOP:
996 case MENU_ACTION_TOGGLE_MUTE:
997 case MENU_ACTION_INCREASE_LISTENING:
998 case MENU_ACTION_DECREASE_LISTENING:
999 case MENU_ACTION_INCREASE_TALKING:
1000 case MENU_ACTION_DECREASE_TALKING:
1001 case MENU_ACTION_RESET_LISTENING:
1002 case MENU_ACTION_RESET_TALKING:
1003 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
1004 case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
1005 case MENU_ACTION_PARTICIPANT_COUNT:
1006 case MENU_ACTION_ADMIN_KICK_LAST:
1007 case MENU_ACTION_LEAVE:
1008 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
1009 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
1011 case MENU_ACTION_PLAYBACK:
1012 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
1013 if (!(ast_strlen_zero(databuf))) {
1014 ast_copy_string(menu_action->data.playback_file, databuf, sizeof(menu_action->data.playback_file));
1016 ast_free(menu_action);
1020 case MENU_ACTION_DIALPLAN_EXEC:
1021 if (!(ast_strlen_zero(databuf))) {
1022 AST_DECLARE_APP_ARGS(args,
1023 AST_APP_ARG(context);
1025 AST_APP_ARG(priority);
1027 AST_STANDARD_APP_ARGS(args, databuf);
1028 if (!ast_strlen_zero(args.context)) {
1029 ast_copy_string(menu_action->data.dialplan_args.context,
1031 sizeof(menu_action->data.dialplan_args.context));
1033 if (!ast_strlen_zero(args.exten)) {
1034 ast_copy_string(menu_action->data.dialplan_args.exten,
1036 sizeof(menu_action->data.dialplan_args.exten));
1038 menu_action->data.dialplan_args.priority = 1; /* 1 by default */
1039 if (!ast_strlen_zero(args.priority) &&
1040 (sscanf(args.priority, "%30u", &menu_action->data.dialplan_args.priority) != 1)) {
1041 /* invalid priority */
1042 ast_free(menu_action);
1046 ast_free(menu_action);
1051 AST_LIST_INSERT_TAIL(&menu_entry->actions, menu_action, action);
1056 static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *action_names)
1058 struct conf_menu_entry *menu_entry = NULL, *cur = NULL;
1060 char *tmp_action_names = ast_strdupa(action_names);
1061 char *action = NULL;
1065 char *delimiter = ",";
1067 if (!(menu_entry = ast_calloc(1, sizeof(*menu_entry)))) {
1075 unsigned int action_len;
1077 if (ast_strlen_zero(tmp_action_names)) {
1080 startbrace = strchr(tmp_action_names, '(');
1081 endbrace = strchr(tmp_action_names, ')');
1082 comma = strchr(tmp_action_names, ',');
1084 /* If the next action has brackets with comma delimited arguments in it,
1085 * make the delimeter ')' instead of a comma to preserve the argments */
1086 if (startbrace && endbrace && comma && (comma > startbrace && comma < endbrace)) {
1092 if (!(action = strsep(&tmp_action_names, delimiter))) {
1096 action = ast_strip(action);
1097 if (ast_strlen_zero(action)) {
1101 action_len = strlen(action);
1102 ast_copy_string(menu_entry->dtmf, dtmf, sizeof(menu_entry->dtmf));
1103 if (!strcasecmp(action, "toggle_mute")) {
1104 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_TOGGLE_MUTE, NULL);
1105 } else if (!strcasecmp(action, "no_op")) {
1106 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_NOOP, NULL);
1107 } else if (!strcasecmp(action, "increase_listening_volume")) {
1108 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_LISTENING, NULL);
1109 } else if (!strcasecmp(action, "decrease_listening_volume")) {
1110 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_LISTENING, NULL);
1111 } else if (!strcasecmp(action, "increase_talking_volume")) {
1112 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_TALKING, NULL);
1113 } else if (!strcasecmp(action, "reset_listening_volume")) {
1114 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_LISTENING, NULL);
1115 } else if (!strcasecmp(action, "reset_talking_volume")) {
1116 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_TALKING, NULL);
1117 } else if (!strcasecmp(action, "decrease_talking_volume")) {
1118 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_TALKING, NULL);
1119 } else if (!strcasecmp(action, "admin_toggle_conference_lock")) {
1120 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_LOCK, NULL);
1121 } else if (!strcasecmp(action, "admin_toggle_mute_participants")) {
1122 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS, NULL);
1123 } else if (!strcasecmp(action, "participant_count")) {
1124 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PARTICIPANT_COUNT, NULL);
1125 } else if (!strcasecmp(action, "admin_kick_last")) {
1126 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_KICK_LAST, NULL);
1127 } else if (!strcasecmp(action, "leave_conference")) {
1128 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_LEAVE, NULL);
1129 } else if (!strcasecmp(action, "set_as_single_video_src")) {
1130 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_SET_SINGLE_VIDEO_SRC, NULL);
1131 } else if (!strcasecmp(action, "release_as_single_video_src")) {
1132 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC, NULL);
1133 } else if (!strncasecmp(action, "dialplan_exec(", 14)) {
1134 ast_copy_string(buf, action, sizeof(buf));
1136 if ((action_args = strchr(action, '('))) {
1139 /* it is possible that this argument may or may not
1140 * have a closing brace at this point, it all depends on if
1141 * comma delimited arguments were provided */
1142 if ((tmp = strchr(action, ')'))) {
1145 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DIALPLAN_EXEC, action_args);
1146 } else if (action_len >= 21 && !strncasecmp(action, "playback_and_continue(", 22)) {
1147 ast_copy_string(buf, action, sizeof(buf));
1149 if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
1153 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK_AND_CONTINUE, action_args);
1154 } else if (action_len >= 8 && !strncasecmp(action, "playback(", 9)) {
1155 ast_copy_string(buf, action, sizeof(buf));
1157 if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
1161 res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK, action_args);
1165 /* if adding any of the actions failed, bail */
1167 struct conf_menu_action *menu_action;
1168 while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
1169 ast_free(menu_action);
1171 ast_free(menu_entry);
1175 /* remove any list entry with an identical DTMF sequence for overrides */
1176 AST_LIST_TRAVERSE_SAFE_BEGIN(&menu->entries, cur, entry) {
1177 if (!strcasecmp(cur->dtmf, menu_entry->dtmf)) {
1178 AST_LIST_REMOVE_CURRENT(entry);
1183 AST_LIST_TRAVERSE_SAFE_END;
1185 AST_LIST_INSERT_TAIL(&menu->entries, menu_entry, entry);
1190 static char *complete_user_profile_name(const char *line, const char *word, int pos, int state)
1194 int wordlen = strlen(word);
1195 struct ao2_iterator i;
1196 struct user_profile *u_profile = NULL;
1197 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
1203 i = ao2_iterator_init(cfg->user_profiles, 0);
1204 while ((u_profile = ao2_iterator_next(&i))) {
1205 if (!strncasecmp(u_profile->name, word, wordlen) && ++which > state) {
1206 res = ast_strdup(u_profile->name);
1207 ao2_ref(u_profile, -1);
1210 ao2_ref(u_profile, -1);
1212 ao2_iterator_destroy(&i);
1217 static char *handle_cli_confbridge_show_user_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1219 struct ao2_iterator it;
1220 struct user_profile *u_profile;
1221 RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
1225 e->command = "confbridge show profile users";
1227 "Usage confbridge show profile users\n";
1233 if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
1237 ast_cli(a->fd,"--------- User Profiles -----------\n");
1238 ao2_lock(cfg->user_profiles);
1239 it = ao2_iterator_init(cfg->user_profiles, 0);
1240 while ((u_profile = ao2_iterator_next(&it))) {
1241 ast_cli(a->fd,"%s\n", u_profile->name);
1242 ao2_ref(u_profile, -1);
1244 ao2_iterator_destroy(&it);
1245 ao2_unlock(cfg->user_profiles);
1249 static char *handle_cli_confbridge_show_user_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1251 struct user_profile u_profile;
1255 e->command = "confbridge show profile user";
1257 "Usage confbridge show profile user [<profile name>]\n";
1261 return complete_user_profile_name(a->line, a->word, a->pos, a->n);
1267 return CLI_SHOWUSAGE;
1270 if (!(conf_find_user_profile(NULL, a->argv[4], &u_profile))) {
1271 ast_cli(a->fd, "No conference user profile named '%s' found!\n", a->argv[4]);
1275 ast_cli(a->fd,"--------------------------------------------\n");
1276 ast_cli(a->fd,"Name: %s\n",
1278 ast_cli(a->fd,"Admin: %s\n",
1279 u_profile.flags & USER_OPT_ADMIN ?
1281 ast_cli(a->fd,"Marked User: %s\n",
1282 u_profile.flags & USER_OPT_MARKEDUSER ?
1284 ast_cli(a->fd,"Start Muted: %s\n",
1285 u_profile.flags & USER_OPT_STARTMUTED?
1287 ast_cli(a->fd,"MOH When Empty: %s\n",
1288 u_profile.flags & USER_OPT_MUSICONHOLD ?
1289 "enabled" : "disabled");
1290 ast_cli(a->fd,"MOH Class: %s\n",
1291 ast_strlen_zero(u_profile.moh_class) ?
1292 "default" : u_profile.moh_class);
1293 ast_cli(a->fd,"Announcement: %s\n",
1294 u_profile.announcement);
1295 ast_cli(a->fd,"Quiet: %s\n",
1296 u_profile.flags & USER_OPT_QUIET ?
1297 "enabled" : "disabled");
1298 ast_cli(a->fd,"Wait Marked: %s\n",
1299 u_profile.flags & USER_OPT_WAITMARKED ?
1300 "enabled" : "disabled");
1301 ast_cli(a->fd,"END Marked: %s\n",
1302 u_profile.flags & USER_OPT_ENDMARKED ?
1303 "enabled" : "disabled");
1304 ast_cli(a->fd,"Drop_silence: %s\n",
1305 u_profile.flags & USER_OPT_DROP_SILENCE ?
1306 "enabled" : "disabled");
1307 ast_cli(a->fd,"Silence Threshold: %dms\n",
1308 u_profile.silence_threshold);
1309 ast_cli(a->fd,"Talking Threshold: %dms\n",
1310 u_profile.talking_threshold);
1311 ast_cli(a->fd,"Denoise: %s\n",
1312 u_profile.flags & USER_OPT_DENOISE ?
1313 "enabled" : "disabled");
1314 ast_cli(a->fd,"Jitterbuffer: %s\n",
1315 u_profile.flags & USER_OPT_JITTERBUFFER ?
1316 "enabled" : "disabled");
1317 ast_cli(a->fd,"Talk Detect Events: %s\n",
1318 u_profile.flags & USER_OPT_TALKER_DETECT ?
1319 "enabled" : "disabled");
1320 ast_cli(a->fd,"DTMF Pass Through: %s\n",
1321 u_profile.flags & USER_OPT_DTMF_PASS ?
1322 "enabled" : "disabled");
1323 ast_cli(a->fd,"PIN: %s\n",
1324 ast_strlen_zero(u_profile.pin) ?
1325 "None" : u_profile.pin);
1326 ast_cli(a->fd,"Announce User Count: %s\n",
1327 u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNT ?
1328 "enabled" : "disabled");
1329 ast_cli(a->fd,"Announce join/leave: %s\n",
1330 u_profile.flags & USER_OPT_ANNOUNCE_JOIN_LEAVE ?
1331 "enabled" : "disabled");
1332 ast_cli(a->fd,"Announce User Count all: %s\n",
1333 u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNTALL ?
1334 "enabled" : "disabled");
1335 ast_cli(a->fd,"\n");
1340 static char *complete_bridge_profile_name(const char *line, const char *word, int pos, int state)
1344 int wordlen = strlen(word);
1345 struct ao2_iterator i;
1346 struct bridge_profile *b_profile = NULL;
1347 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
1353 i = ao2_iterator_init(cfg->bridge_profiles, 0);
1354 while ((b_profile = ao2_iterator_next(&i))) {
1355 if (!strncasecmp(b_profile->name, word, wordlen) && ++which > state) {
1356 res = ast_strdup(b_profile->name);
1357 ao2_ref(b_profile, -1);
1360 ao2_ref(b_profile, -1);
1362 ao2_iterator_destroy(&i);
1367 static char *handle_cli_confbridge_show_bridge_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1369 struct ao2_iterator it;
1370 struct bridge_profile *b_profile;
1371 RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
1375 e->command = "confbridge show profile bridges";
1377 "Usage confbridge show profile bridges\n";
1383 if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
1387 ast_cli(a->fd,"--------- Bridge Profiles -----------\n");
1388 ao2_lock(cfg->bridge_profiles);
1389 it = ao2_iterator_init(cfg->bridge_profiles, 0);
1390 while ((b_profile = ao2_iterator_next(&it))) {
1391 ast_cli(a->fd,"%s\n", b_profile->name);
1392 ao2_ref(b_profile, -1);
1394 ao2_iterator_destroy(&it);
1395 ao2_unlock(cfg->bridge_profiles);
1400 static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1402 struct bridge_profile b_profile;
1407 e->command = "confbridge show profile bridge";
1409 "Usage confbridge show profile bridge <profile name>\n";
1413 return complete_bridge_profile_name(a->line, a->word, a->pos, a->n);
1419 return CLI_SHOWUSAGE;
1422 if (!(conf_find_bridge_profile(NULL, a->argv[4], &b_profile))) {
1423 ast_cli(a->fd, "No conference bridge profile named '%s' found!\n", a->argv[4]);
1427 ast_cli(a->fd,"--------------------------------------------\n");
1428 ast_cli(a->fd,"Name: %s\n", b_profile.name);
1429 ast_cli(a->fd,"Language: %s\n", b_profile.language);
1431 if (b_profile.internal_sample_rate) {
1432 snprintf(tmp, sizeof(tmp), "%d", b_profile.internal_sample_rate);
1434 ast_copy_string(tmp, "auto", sizeof(tmp));
1436 ast_cli(a->fd,"Internal Sample Rate: %s\n", tmp);
1438 if (b_profile.mix_interval) {
1439 ast_cli(a->fd,"Mixing Interval: %d\n", b_profile.mix_interval);
1441 ast_cli(a->fd,"Mixing Interval: Default 20ms\n");
1444 ast_cli(a->fd,"Record Conference: %s\n",
1445 b_profile.flags & BRIDGE_OPT_RECORD_CONFERENCE ?
1448 ast_cli(a->fd,"Record File Append: %s\n",
1449 b_profile.flags & BRIDGE_OPT_RECORD_FILE_APPEND ?
1452 ast_cli(a->fd,"Record File: %s\n",
1453 ast_strlen_zero(b_profile.rec_file) ? "Auto Generated" :
1454 b_profile.rec_file);
1456 if (b_profile.max_members) {
1457 ast_cli(a->fd,"Max Members: %d\n", b_profile.max_members);
1459 ast_cli(a->fd,"Max Members: No Limit\n");
1462 switch (b_profile.flags
1463 & (BRIDGE_OPT_VIDEO_SRC_LAST_MARKED | BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
1464 | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
1465 case BRIDGE_OPT_VIDEO_SRC_LAST_MARKED:
1466 ast_cli(a->fd, "Video Mode: last_marked\n");
1468 case BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED:
1469 ast_cli(a->fd, "Video Mode: first_marked\n");
1471 case BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER:
1472 ast_cli(a->fd, "Video Mode: follow_talker\n");
1475 ast_cli(a->fd, "Video Mode: no video\n");
1478 /* Opps. We have more than one video mode flag set. */
1483 ast_cli(a->fd,"sound_only_person: %s\n", conf_get_sound(CONF_SOUND_ONLY_PERSON, b_profile.sounds));
1484 ast_cli(a->fd,"sound_only_one: %s\n", conf_get_sound(CONF_SOUND_ONLY_ONE, b_profile.sounds));
1485 ast_cli(a->fd,"sound_has_joined: %s\n", conf_get_sound(CONF_SOUND_HAS_JOINED, b_profile.sounds));
1486 ast_cli(a->fd,"sound_has_left: %s\n", conf_get_sound(CONF_SOUND_HAS_LEFT, b_profile.sounds));
1487 ast_cli(a->fd,"sound_kicked: %s\n", conf_get_sound(CONF_SOUND_KICKED, b_profile.sounds));
1488 ast_cli(a->fd,"sound_muted: %s\n", conf_get_sound(CONF_SOUND_MUTED, b_profile.sounds));
1489 ast_cli(a->fd,"sound_unmuted: %s\n", conf_get_sound(CONF_SOUND_UNMUTED, b_profile.sounds));
1490 ast_cli(a->fd,"sound_there_are: %s\n", conf_get_sound(CONF_SOUND_THERE_ARE, b_profile.sounds));
1491 ast_cli(a->fd,"sound_other_in_party: %s\n", conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, b_profile.sounds));
1492 ast_cli(a->fd,"sound_place_into_conference: %s\n", conf_get_sound(CONF_SOUND_PLACE_IN_CONF, b_profile.sounds));
1493 ast_cli(a->fd,"sound_wait_for_leader: %s\n", conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, b_profile.sounds));
1494 ast_cli(a->fd,"sound_leader_has_left: %s\n", conf_get_sound(CONF_SOUND_LEADER_HAS_LEFT, b_profile.sounds));
1495 ast_cli(a->fd,"sound_get_pin: %s\n", conf_get_sound(CONF_SOUND_GET_PIN, b_profile.sounds));
1496 ast_cli(a->fd,"sound_invalid_pin: %s\n", conf_get_sound(CONF_SOUND_INVALID_PIN, b_profile.sounds));
1497 ast_cli(a->fd,"sound_locked: %s\n", conf_get_sound(CONF_SOUND_LOCKED, b_profile.sounds));
1498 ast_cli(a->fd,"sound_unlocked_now: %s\n", conf_get_sound(CONF_SOUND_UNLOCKED_NOW, b_profile.sounds));
1499 ast_cli(a->fd,"sound_lockednow: %s\n", conf_get_sound(CONF_SOUND_LOCKED_NOW, b_profile.sounds));
1500 ast_cli(a->fd,"sound_error_menu: %s\n", conf_get_sound(CONF_SOUND_ERROR_MENU, b_profile.sounds));
1501 ast_cli(a->fd,"sound_join: %s\n", conf_get_sound(CONF_SOUND_JOIN, b_profile.sounds));
1502 ast_cli(a->fd,"sound_leave: %s\n", conf_get_sound(CONF_SOUND_LEAVE, b_profile.sounds));
1503 ast_cli(a->fd,"sound_participants_muted: %s\n", conf_get_sound(CONF_SOUND_PARTICIPANTS_MUTED, b_profile.sounds));
1504 ast_cli(a->fd,"sound_participants_unmuted: %s\n", conf_get_sound(CONF_SOUND_PARTICIPANTS_UNMUTED, b_profile.sounds));
1505 ast_cli(a->fd,"\n");
1507 conf_bridge_profile_destroy(&b_profile);
1511 static char *complete_menu_name(const char *line, const char *word, int pos, int state)
1515 int wordlen = strlen(word);
1516 struct ao2_iterator i;
1517 struct conf_menu *menu = NULL;
1518 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
1524 i = ao2_iterator_init(cfg->menus, 0);
1525 while ((menu = ao2_iterator_next(&i))) {
1526 if (!strncasecmp(menu->name, word, wordlen) && ++which > state) {
1527 res = ast_strdup(menu->name);
1533 ao2_iterator_destroy(&i);
1538 static char *handle_cli_confbridge_show_menus(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1540 struct ao2_iterator it;
1541 struct conf_menu *menu;
1542 RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
1546 e->command = "confbridge show menus";
1548 "Usage confbridge show profile menus\n";
1554 if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
1558 ast_cli(a->fd,"--------- Menus -----------\n");
1559 ao2_lock(cfg->menus);
1560 it = ao2_iterator_init(cfg->menus, 0);
1561 while ((menu = ao2_iterator_next(&it))) {
1562 ast_cli(a->fd,"%s\n", menu->name);
1565 ao2_iterator_destroy(&it);
1566 ao2_unlock(cfg->menus);
1571 static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1573 RAII_VAR(struct conf_menu *, menu, NULL, ao2_cleanup);
1574 RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
1575 struct conf_menu_entry *menu_entry = NULL;
1576 struct conf_menu_action *menu_action = NULL;
1580 e->command = "confbridge show menu";
1582 "Usage confbridge show menu [<menu name>]\n";
1586 return complete_menu_name(a->line, a->word, a->pos, a->n);
1592 return CLI_SHOWUSAGE;
1595 if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
1599 if (!(menu = menu_find(cfg->menus, a->argv[3]))) {
1600 ast_cli(a->fd, "No conference menu named '%s' found!\n", a->argv[3]);
1605 ast_cli(a->fd,"Name: %s\n", menu->name);
1606 AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
1608 ast_cli(a->fd, "%s=", menu_entry->dtmf);
1609 AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
1611 ast_cli(a->fd, ", ");
1613 switch (menu_action->id) {
1614 case MENU_ACTION_TOGGLE_MUTE:
1615 ast_cli(a->fd, "toggle_mute");
1617 case MENU_ACTION_NOOP:
1618 ast_cli(a->fd, "no_op");
1620 case MENU_ACTION_INCREASE_LISTENING:
1621 ast_cli(a->fd, "increase_listening_volume");
1623 case MENU_ACTION_DECREASE_LISTENING:
1624 ast_cli(a->fd, "decrease_listening_volume");
1626 case MENU_ACTION_RESET_LISTENING:
1627 ast_cli(a->fd, "reset_listening_volume");
1629 case MENU_ACTION_RESET_TALKING:
1630 ast_cli(a->fd, "reset_talking_volume");
1632 case MENU_ACTION_INCREASE_TALKING:
1633 ast_cli(a->fd, "increase_talking_volume");
1635 case MENU_ACTION_DECREASE_TALKING:
1636 ast_cli(a->fd, "decrease_talking_volume");
1638 case MENU_ACTION_PLAYBACK:
1639 ast_cli(a->fd, "playback(%s)", menu_action->data.playback_file);
1641 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
1642 ast_cli(a->fd, "playback_and_continue(%s)", menu_action->data.playback_file);
1644 case MENU_ACTION_DIALPLAN_EXEC:
1645 ast_cli(a->fd, "dialplan_exec(%s,%s,%d)",
1646 menu_action->data.dialplan_args.context,
1647 menu_action->data.dialplan_args.exten,
1648 menu_action->data.dialplan_args.priority);
1650 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
1651 ast_cli(a->fd, "admin_toggle_conference_lock");
1653 case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
1654 ast_cli(a->fd, "admin_toggle_mute_participants");
1656 case MENU_ACTION_PARTICIPANT_COUNT:
1657 ast_cli(a->fd, "participant_count");
1659 case MENU_ACTION_ADMIN_KICK_LAST:
1660 ast_cli(a->fd, "admin_kick_last");
1662 case MENU_ACTION_LEAVE:
1663 ast_cli(a->fd, "leave_conference");
1665 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
1666 ast_cli(a->fd, "set_as_single_video_src");
1668 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
1669 ast_cli(a->fd, "release_as_single_video_src");
1674 ast_cli(a->fd,"\n");
1682 static struct ast_cli_entry cli_confbridge_parser[] = {
1683 AST_CLI_DEFINE(handle_cli_confbridge_show_user_profile, "Show a conference user profile."),
1684 AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profile, "Show a conference bridge profile."),
1685 AST_CLI_DEFINE(handle_cli_confbridge_show_menu, "Show a conference menu"),
1686 AST_CLI_DEFINE(handle_cli_confbridge_show_user_profiles, "Show a list of conference user profiles."),
1687 AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profiles, "Show a list of conference bridge profiles."),
1688 AST_CLI_DEFINE(handle_cli_confbridge_show_menus, "Show a list of conference menus"),
1692 static void confbridge_cfg_destructor(void *obj)
1694 struct confbridge_cfg *cfg = obj;
1695 ao2_cleanup(cfg->user_profiles);
1696 ao2_cleanup(cfg->bridge_profiles);
1697 ao2_cleanup(cfg->menus);
1700 void *confbridge_cfg_alloc(void)
1702 struct confbridge_cfg *cfg;
1704 if (!(cfg = ao2_alloc(sizeof(*cfg), confbridge_cfg_destructor))) {
1708 if (!(cfg->user_profiles = ao2_container_alloc(283, user_hash_cb, user_cmp_cb))) {
1712 if (!(cfg->bridge_profiles = ao2_container_alloc(283, bridge_hash_cb, bridge_cmp_cb))) {
1716 if (!(cfg->menus = ao2_container_alloc(283, menu_hash_cb, menu_cmp_cb))) {
1726 static int announce_user_count_all_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1728 struct user_profile *u_profile = obj;
1730 if (strcasecmp(var->name, "announce_user_count_all")) {
1733 if (ast_true(var->value)) {
1734 u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL;
1735 } else if (ast_false(var->value)) {
1736 u_profile->flags = u_profile->flags & ~USER_OPT_ANNOUNCEUSERCOUNTALL;
1737 } else if (sscanf(var->value, "%30u", &u_profile->announce_user_count_all_after) == 1) {
1738 u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL;
1745 static int mix_interval_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1747 struct bridge_profile *b_profile = obj;
1749 if (strcasecmp(var->name, "mixing_interval")) {
1752 if (sscanf(var->value, "%30u", &b_profile->mix_interval) != 1) {
1755 switch (b_profile->mix_interval) {
1766 static int video_mode_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1768 struct bridge_profile *b_profile = obj;
1770 if (strcasecmp(var->name, "video_mode")) {
1773 if (!strcasecmp(var->value, "first_marked")) {
1774 ast_set_flags_to(b_profile,
1775 BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
1776 | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
1777 | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER,
1778 BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED);
1779 } else if (!strcasecmp(var->value, "last_marked")) {
1780 ast_set_flags_to(b_profile,
1781 BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
1782 | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
1783 | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER,
1784 BRIDGE_OPT_VIDEO_SRC_LAST_MARKED);
1785 } else if (!strcasecmp(var->value, "follow_talker")) {
1786 ast_set_flags_to(b_profile,
1787 BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
1788 | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
1789 | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER,
1790 BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
1791 } else if (!strcasecmp(var->value, "none")) {
1792 ast_clear_flag(b_profile,
1793 BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
1794 | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
1795 | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
1802 static int user_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1804 struct user_profile *u_profile = obj;
1806 return conf_find_user_profile(NULL, var->value, u_profile) ? 0 : -1;
1809 static int bridge_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1811 struct bridge_profile *b_profile = obj;
1812 struct bridge_profile_sounds *sounds = bridge_profile_sounds_alloc();
1813 struct bridge_profile_sounds *oldsounds = b_profile->sounds;
1818 if (!(conf_find_bridge_profile(NULL, var->value, b_profile))) {
1819 ao2_ref(sounds, -1);
1822 /* Using a bridge profile as a template is a little complicated due to the sounds. Since the sounds
1823 * structure of a dynamic profile will need to be altered, a completely new sounds structure must be
1824 * created instead of simply holding a reference to the one built by the config file. */
1825 ast_string_field_set(sounds, onlyperson, b_profile->sounds->onlyperson);
1826 ast_string_field_set(sounds, onlyone, b_profile->sounds->onlyone);
1827 ast_string_field_set(sounds, hasjoin, b_profile->sounds->hasjoin);
1828 ast_string_field_set(sounds, hasleft, b_profile->sounds->hasleft);
1829 ast_string_field_set(sounds, kicked, b_profile->sounds->kicked);
1830 ast_string_field_set(sounds, muted, b_profile->sounds->muted);
1831 ast_string_field_set(sounds, unmuted, b_profile->sounds->unmuted);
1832 ast_string_field_set(sounds, thereare, b_profile->sounds->thereare);
1833 ast_string_field_set(sounds, otherinparty, b_profile->sounds->otherinparty);
1834 ast_string_field_set(sounds, placeintoconf, b_profile->sounds->placeintoconf);
1835 ast_string_field_set(sounds, waitforleader, b_profile->sounds->waitforleader);
1836 ast_string_field_set(sounds, leaderhasleft, b_profile->sounds->leaderhasleft);
1837 ast_string_field_set(sounds, getpin, b_profile->sounds->getpin);
1838 ast_string_field_set(sounds, invalidpin, b_profile->sounds->invalidpin);
1839 ast_string_field_set(sounds, locked, b_profile->sounds->locked);
1840 ast_string_field_set(sounds, unlockednow, b_profile->sounds->unlockednow);
1841 ast_string_field_set(sounds, lockednow, b_profile->sounds->lockednow);
1842 ast_string_field_set(sounds, errormenu, b_profile->sounds->errormenu);
1843 ast_string_field_set(sounds, join, b_profile->sounds->join);
1844 ast_string_field_set(sounds, leave, b_profile->sounds->leave);
1845 ast_string_field_set(sounds, participantsmuted, b_profile->sounds->participantsmuted);
1846 ast_string_field_set(sounds, participantsunmuted, b_profile->sounds->participantsunmuted);
1848 ao2_ref(b_profile->sounds, -1); /* sounds struct copied over to it from the template by reference only. */
1849 ao2_ref(oldsounds, -1); /* original sounds struct we don't need anymore */
1850 b_profile->sounds = sounds; /* the new sounds struct that is a deep copy of the one from the template. */
1855 static int sound_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1857 set_sound(var->name, var->value, obj);
1861 static int menu_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1863 add_menu_entry(obj, var->name, var->value);
1867 static int verify_default_profiles(void)
1869 RAII_VAR(struct user_profile *, user_profile, NULL, ao2_cleanup);
1870 RAII_VAR(struct bridge_profile *, bridge_profile, NULL, ao2_cleanup);
1871 struct confbridge_cfg *cfg = aco_pending_config(&cfg_info);
1877 bridge_profile = ao2_find(cfg->bridge_profiles, DEFAULT_BRIDGE_PROFILE, OBJ_KEY);
1878 if (!bridge_profile) {
1879 bridge_profile = bridge_profile_alloc(DEFAULT_BRIDGE_PROFILE);
1880 if (!bridge_profile) {
1883 ast_log(AST_LOG_NOTICE, "Adding %s profile to app_confbridge\n", DEFAULT_BRIDGE_PROFILE);
1884 aco_set_defaults(&bridge_type, DEFAULT_BRIDGE_PROFILE, bridge_profile);
1885 ao2_link(cfg->bridge_profiles, bridge_profile);
1888 user_profile = ao2_find(cfg->user_profiles, DEFAULT_USER_PROFILE, OBJ_KEY);
1889 if (!user_profile) {
1890 user_profile = user_profile_alloc(DEFAULT_USER_PROFILE);
1891 if (!user_profile) {
1894 ast_log(AST_LOG_NOTICE, "Adding %s profile to app_confbridge\n", DEFAULT_USER_PROFILE);
1895 aco_set_defaults(&user_type, DEFAULT_USER_PROFILE, user_profile);
1896 ao2_link(cfg->user_profiles, user_profile);
1902 int conf_load_config(void)
1904 if (aco_info_init(&cfg_info)) {
1909 aco_option_register(&cfg_info, "type", ACO_EXACT, user_types, NULL, OPT_NOOP_T, 0, 0);
1910 aco_option_register(&cfg_info, "admin", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ADMIN);
1911 aco_option_register(&cfg_info, "marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_MARKEDUSER);
1912 aco_option_register(&cfg_info, "startmuted", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_STARTMUTED);
1913 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);
1914 aco_option_register(&cfg_info, "quiet", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_QUIET);
1915 aco_option_register_custom(&cfg_info, "announce_user_count_all", ACO_EXACT, user_types, "no", announce_user_count_all_handler, 0);
1916 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);
1917 /* Negative logic. Defaults to "yes" and evaluates with ast_false(). If !ast_false(), USER_OPT_NOONLYPERSON is cleared */
1918 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);
1919 aco_option_register(&cfg_info, "wait_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_WAITMARKED);
1920 aco_option_register(&cfg_info, "end_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKED);
1921 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);
1922 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);
1923 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);
1924 aco_option_register(&cfg_info, "pin", ACO_EXACT, user_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct user_profile, pin));
1925 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));
1926 aco_option_register(&cfg_info, "announcement", ACO_EXACT, user_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct user_profile, announcement));
1927 aco_option_register(&cfg_info, "denoise", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_DENOISE);
1928 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);
1929 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));
1930 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));
1931 aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_JITTERBUFFER);
1932 /* This option should only be used with the CONFBRIDGE dialplan function */
1933 aco_option_register_custom(&cfg_info, "template", ACO_EXACT, user_types, NULL, user_template_handler, 0);
1935 /* XXX ASTERISK-21271 need a user supplied bridge merge_priority to merge ConfBridges (default = 1, range 1-INT_MAX) */
1936 /* Bridge options */
1937 aco_option_register(&cfg_info, "type", ACO_EXACT, bridge_types, NULL, OPT_NOOP_T, 0, 0);
1938 aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), USER_OPT_JITTERBUFFER);
1939 /* "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 */
1940 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);
1941 aco_option_register_custom(&cfg_info, "mixing_interval", ACO_EXACT, bridge_types, "20", mix_interval_handler, 0);
1942 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);
1943 aco_option_register_custom(&cfg_info, "video_mode", ACO_EXACT, bridge_types, NULL, video_mode_handler, 0);
1944 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);
1945 aco_option_register(&cfg_info, "max_members", ACO_EXACT, bridge_types, "0", OPT_UINT_T, 0, FLDSET(struct bridge_profile, max_members));
1946 aco_option_register(&cfg_info, "record_file", ACO_EXACT, bridge_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, rec_file));
1947 aco_option_register(&cfg_info, "language", ACO_EXACT, bridge_types, "en", OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, language));
1948 aco_option_register_custom(&cfg_info, "^sound_", ACO_REGEX, bridge_types, NULL, sound_option_handler, 0);
1949 /* This option should only be used with the CONFBRIDGE dialplan function */
1950 aco_option_register_custom(&cfg_info, "template", ACO_EXACT, bridge_types, NULL, bridge_template_handler, 0);
1953 aco_option_register(&cfg_info, "type", ACO_EXACT, menu_types, NULL, OPT_NOOP_T, 0, 0);
1954 aco_option_register_custom(&cfg_info, "^[0-9A-D*#]+$", ACO_REGEX, menu_types, NULL, menu_option_handler, 0);
1956 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1960 if (ast_cli_register_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser))) {
1966 conf_destroy_config();
1970 int conf_reload_config(void)
1972 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
1973 /* On a reload, just keep the config we already have in place. */
1979 static void conf_user_profile_copy(struct user_profile *dst, struct user_profile *src)
1984 const struct user_profile *conf_find_user_profile(struct ast_channel *chan, const char *user_profile_name, struct user_profile *result)
1986 struct user_profile *tmp2;
1987 struct ast_datastore *datastore = NULL;
1988 struct func_confbridge_data *b_data = NULL;
1989 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
1996 ast_channel_lock(chan);
1997 datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
1998 ast_channel_unlock(chan);
2000 b_data = datastore->data;
2001 if (b_data->u_usable) {
2002 conf_user_profile_copy(result, &b_data->u_profile);
2008 if (ast_strlen_zero(user_profile_name)) {
2009 user_profile_name = DEFAULT_USER_PROFILE;
2011 if (!(tmp2 = ao2_find(cfg->user_profiles, user_profile_name, OBJ_KEY))) {
2015 conf_user_profile_copy(result, tmp2);
2022 void conf_bridge_profile_copy(struct bridge_profile *dst, struct bridge_profile *src)
2026 ao2_ref(src->sounds, +1);
2030 void conf_bridge_profile_destroy(struct bridge_profile *b_profile)
2032 if (b_profile->sounds) {
2033 ao2_ref(b_profile->sounds, -1);
2034 b_profile->sounds = NULL;
2038 const struct bridge_profile *conf_find_bridge_profile(struct ast_channel *chan, const char *bridge_profile_name, struct bridge_profile *result)
2040 struct bridge_profile *tmp2;
2041 struct ast_datastore *datastore = NULL;
2042 struct func_confbridge_data *b_data = NULL;
2043 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
2050 ast_channel_lock(chan);
2051 datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
2052 ast_channel_unlock(chan);
2054 b_data = datastore->data;
2055 if (b_data->b_usable) {
2056 conf_bridge_profile_copy(result, &b_data->b_profile);
2061 if (ast_strlen_zero(bridge_profile_name)) {
2062 bridge_profile_name = DEFAULT_BRIDGE_PROFILE;
2064 if (!(tmp2 = ao2_find(cfg->bridge_profiles, bridge_profile_name, OBJ_KEY))) {
2068 conf_bridge_profile_copy(result, tmp2);
2075 struct dtmf_menu_hook_pvt {
2076 struct confbridge_user *user;
2077 struct conf_menu_entry menu_entry;
2078 struct conf_menu *menu;
2081 static void menu_hook_destroy(void *hook_pvt)
2083 struct dtmf_menu_hook_pvt *pvt = hook_pvt;
2084 struct conf_menu_action *action = NULL;
2086 ao2_ref(pvt->menu, -1);
2088 while ((action = AST_LIST_REMOVE_HEAD(&pvt->menu_entry.actions, action))) {
2094 static int menu_hook_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
2096 struct dtmf_menu_hook_pvt *pvt = hook_pvt;
2098 return conf_handle_dtmf(bridge_channel, pvt->user, &pvt->menu_entry, pvt->menu);
2101 static int copy_menu_entry(struct conf_menu_entry *dst, struct conf_menu_entry *src)
2103 struct conf_menu_action *menu_action = NULL;
2104 struct conf_menu_action *new_menu_action = NULL;
2106 memcpy(dst, src, sizeof(*dst));
2107 AST_LIST_HEAD_INIT_NOLOCK(&dst->actions);
2109 AST_LIST_TRAVERSE(&src->actions, menu_action, action) {
2110 if (!(new_menu_action = ast_calloc(1, sizeof(*new_menu_action)))) {
2113 memcpy(new_menu_action, menu_action, sizeof(*new_menu_action));
2114 AST_LIST_INSERT_HEAD(&dst->actions, new_menu_action, action);
2119 void conf_menu_entry_destroy(struct conf_menu_entry *menu_entry)
2121 struct conf_menu_action *menu_action = NULL;
2122 while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
2123 ast_free(menu_action);
2127 int conf_find_menu_entry_by_sequence(const char *dtmf_sequence, struct conf_menu *menu, struct conf_menu_entry *result)
2129 struct conf_menu_entry *menu_entry = NULL;
2132 AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
2133 if (!strcasecmp(menu_entry->dtmf, dtmf_sequence)) {
2134 copy_menu_entry(result, menu_entry);
2144 int conf_set_menu_to_user(const char *menu_name, struct confbridge_user *user)
2146 struct conf_menu *menu;
2147 struct conf_menu_entry *menu_entry = NULL;
2148 RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
2154 if (!(menu = menu_find(cfg->menus, menu_name))) {
2158 AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
2159 struct dtmf_menu_hook_pvt *pvt;
2161 if (!(pvt = ast_calloc(1, sizeof(*pvt)))) {
2166 if (copy_menu_entry(&pvt->menu_entry, menu_entry)) {
2176 if (ast_bridge_dtmf_hook(&user->features, pvt->menu_entry.dtmf,
2177 menu_hook_callback, pvt, menu_hook_destroy, 0)) {
2178 menu_hook_destroy(pvt);
2188 void conf_destroy_config(void)
2190 ast_cli_unregister_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser));
2191 aco_info_destroy(&cfg_info);
2192 ao2_global_obj_release(cfg_handle);