Asterisk - The Open Source Telephony Project  21.4.1
conf_config_parser.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2011, Digium, Inc.
5  *
6  * David Vossel <dvossel@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  *
21  * \brief ConfBridge config parser
22  *
23  * \author David Vossel <dvossel@digium.com>
24  */
25 
26 /*** MODULEINFO
27  <support_level>core</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 #include "asterisk/logger.h"
33 #include "asterisk/config.h"
35 #include "include/confbridge.h"
36 #include "asterisk/astobj2.h"
37 #include "asterisk/cli.h"
39 #include "asterisk/stringfields.h"
40 #include "asterisk/pbx.h"
41 
42 
43 /*** DOCUMENTATION
44  <configInfo name="app_confbridge" language="en_US">
45  <synopsis>Conference Bridge Application</synopsis>
46  <configFile name="confbridge.conf">
47  <configObject name="global">
48  <synopsis>Unused, but reserved.</synopsis>
49  </configObject>
50  <configObject name="user_profile">
51  <synopsis>A named profile to apply to specific callers.</synopsis>
52  <description><para>Callers in a ConfBridge have a profile associated with them
53  that determine their options. A configuration section is determined to be a
54  user_profile when the <literal>type</literal> parameter has a value
55  of <literal>user</literal>.
56  </para></description>
57  <configOption name="type">
58  <synopsis>Define this configuration category as a user profile.</synopsis>
59  <description><para>The type parameter determines how a context in the
60  configuration file is interpreted.</para>
61  <enumlist>
62  <enum name="user"><para>Configure the context as a <replaceable>user_profile</replaceable></para></enum>
63  <enum name="bridge"><para>Configure the context as a <replaceable>bridge_profile</replaceable></para></enum>
64  <enum name="menu"><para>Configure the context as a <replaceable>menu</replaceable></para></enum>
65  </enumlist>
66  </description>
67  </configOption>
68  <configOption name="admin">
69  <synopsis>Sets if the user is an admin or not</synopsis>
70  </configOption>
71  <configOption name="send_events" default="no">
72  <synopsis>Sets if events are send to the user</synopsis>
73  <description><para>If events are enabled for this bridge and this option is
74  set, users will receive events like join, leave, talking, etc. via text
75  messages. For users accessing the bridge via chan_pjsip, this means
76  in-dialog MESSAGE messages. This is most useful for WebRTC participants
77  where the browser application can use the messages to alter the user
78  interface.</para></description>
79  </configOption>
80  <configOption name="echo_events" default="yes">
81  <synopsis>Sets if events are echoed back to the user that
82  triggered them</synopsis>
83  <description><para>If events are enabled for this user and this option
84  is set, the user will receive events they trigger, talking, mute, etc.
85  If not set, they will not receive their own events.
86  </para></description>
87  </configOption>
88  <configOption name="marked">
89  <synopsis>Sets if this is a marked user or not</synopsis>
90  </configOption>
91  <configOption name="startmuted">
92  <synopsis>Sets if all users should start out muted</synopsis>
93  </configOption>
94  <configOption name="music_on_hold_when_empty">
95  <synopsis>Play MOH when user is alone or waiting on a marked user</synopsis>
96  </configOption>
97  <configOption name="quiet">
98  <synopsis>Silence enter/leave prompts and user intros for this user</synopsis>
99  </configOption>
100  <configOption name="hear_own_join_sound">
101  <synopsis>Determines if the user also hears the join sound when they enter a conference</synopsis>
102  </configOption>
103  <configOption name="announce_user_count">
104  <synopsis>Sets if the number of users should be announced to the user</synopsis>
105  </configOption>
106  <configOption name="announce_user_count_all">
107  <synopsis>Announce user count to all the other users when this user joins</synopsis>
108  <description><para>Sets if the number of users should be announced to all the other users
109  in the conference when this user joins. This option can be either set to 'yes' or
110  a number. When set to a number, the announcement will only occur once the user
111  count is above the specified number.
112  </para></description>
113  </configOption>
114  <configOption name="announce_only_user">
115  <synopsis>Announce to a user when they join an empty conference</synopsis>
116  </configOption>
117  <configOption name="wait_marked">
118  <synopsis>Sets if the user must wait for a marked user to enter before joining a conference</synopsis>
119  </configOption>
120  <configOption name="end_marked">
121  <synopsis>Kick the user from the conference when the last marked user leaves</synopsis>
122  </configOption>
123  <configOption name="end_marked_any">
124  <synopsis>Kick the user from the conference when any marked user leaves</synopsis>
125  </configOption>
126  <configOption name="talk_detection_events">
127  <synopsis>Set whether or not notifications of when a user begins and ends talking should be sent out as events over AMI</synopsis>
128  </configOption>
129  <configOption name="dtmf_passthrough">
130  <synopsis>Sets whether or not DTMF should pass through the conference</synopsis>
131  </configOption>
132  <configOption name="announce_join_leave">
133  <synopsis>Prompt user for their name when joining a conference and play it to the conference when they enter</synopsis>
134  </configOption>
135  <configOption name="announce_join_leave_review">
136  <synopsis>Prompt user for their name when joining a conference and play it to the conference when they enter.
137  The user will be asked to review the recording of their name before entering the conference.</synopsis>
138  </configOption>
139  <configOption name="pin">
140  <synopsis>Sets a PIN the user must enter before joining the conference</synopsis>
141  </configOption>
142  <configOption name="music_on_hold_class">
143  <synopsis>The MOH class to use for this user</synopsis>
144  </configOption>
145  <configOption name="announcement">
146  <synopsis>Sound file to play to the user when they join a conference</synopsis>
147  </configOption>
148  <configOption name="denoise">
149  <synopsis>Apply a denoise filter to the audio before mixing</synopsis>
150  <description><para>Sets whether or not a denoise filter should be applied
151  to the audio before mixing or not. Off by default. Requires
152  <literal>codec_speex</literal> to be built and installed. Do not confuse this option
153  with <replaceable>drop_silence</replaceable>. Denoise is useful if there is a lot of background
154  noise for a user as it attempts to remove the noise while preserving
155  the speech. This option does NOT remove silence from being mixed into
156  the conference and does come at the cost of a slight performance hit.
157  </para></description>
158  </configOption>
159  <configOption name="dsp_drop_silence">
160  <synopsis>Drop what Asterisk detects as silence from audio sent to the bridge</synopsis>
161  <description><para>
162  This option drops what Asterisk detects as silence from
163  entering into the bridge. Enabling this option will drastically
164  improve performance and help remove the buildup of background
165  noise from the conference. Highly recommended for large conferences
166  due to its performance enhancements.
167  </para></description>
168  </configOption>
169  <configOption name="dsp_silence_threshold">
170  <synopsis>The number of milliseconds of silence necessary to declare talking stopped.</synopsis>
171  <description>
172  <para>The time in milliseconds of sound falling below the
173  <replaceable>dsp_talking_threshold</replaceable> option when
174  a user is considered to stop talking. This value affects several
175  operations and should not be changed unless the impact on call
176  quality is fully understood.
177  </para>
178  <para>What this value affects internally:
179  </para>
180  <para>1. When talk detection AMI events are enabled, this value
181  determines when the user has stopped talking after a
182  period of talking. If this value is set too low
183  AMI events indicating the user has stopped talking
184  may get falsely sent out when the user briefly pauses
185  during mid sentence.
186  </para>
187  <para>2. The <replaceable>drop_silence</replaceable> option
188  depends on this value to determine when the user's audio should
189  begin to be dropped from the conference bridge after the user
190  stops talking. If this value is set too low the user's
191  audio stream may sound choppy to the other participants. This
192  is caused by the user transitioning constantly from silence to
193  talking during mid sentence.
194  </para>
195  <para>The best way to approach this option is to set it slightly
196  above the maximum amount of milliseconds of silence a user may
197  generate during natural speech.
198  </para>
199  <para>Valid values are 1 through 2^31.</para>
200  </description>
201  </configOption>
202  <configOption name="dsp_talking_threshold">
203  <synopsis>Average magnitude threshold to determine talking.</synopsis>
204  <description>
205  <para>The minimum average magnitude per sample in a frame
206  for the DSP to consider talking/noise present. A value below
207  this level is considered silence. This value affects several
208  operations and should not be changed unless the impact on call
209  quality is fully understood.
210  </para>
211  <para>What this value affects internally:
212  </para>
213  <para>1. Audio is only mixed out of a user's incoming audio
214  stream if talking is detected. If this value is set too
215  high the user will hear himself talking.
216  </para>
217  <para>2. When talk detection AMI events are enabled, this value
218  determines when talking has begun which results in
219  an AMI event to fire. If this value is set too low
220  AMI events may be falsely triggered by variants in
221  room noise.
222  </para>
223  <para>3. The <replaceable>drop_silence</replaceable> option
224  depends on this value to determine when the user's audio should
225  be mixed into the bridge after periods of silence. If this value
226  is too high the user's speech will get discarded as they will
227  be considered silent.
228  </para>
229  <para>Valid values are 1 through 2^15.</para>
230  </description>
231  </configOption>
232  <configOption name="jitterbuffer">
233  <synopsis>Place a jitter buffer on the user's audio stream before audio mixing is performed</synopsis>
234  <description><para>
235  Enabling this option places a jitterbuffer on the user's audio stream
236  before audio mixing is performed. This is highly recommended but will
237  add a slight delay to the audio. This option is using the <literal>JITTERBUFFER</literal>
238  dialplan function's default adaptive jitterbuffer. For a more fine tuned
239  jitterbuffer, disable this option and use the <literal>JITTERBUFFER</literal> dialplan function
240  on the user before entering the ConfBridge application.
241  </para></description>
242  </configOption>
243  <configOption name="template">
244  <synopsis>When using the CONFBRIDGE dialplan function, use a user profile as a template for creating a new temporary profile</synopsis>
245  </configOption>
246  <configOption name="timeout">
247  <synopsis>Kick the user out of the conference after this many seconds. 0 means there is no timeout for the user.</synopsis>
248  </configOption>
249  <configOption name="text_messaging" default="yes">
250  <synopsis>Sets if text messages are sent to the user.</synopsis>
251  <description><para>If text messaging is enabled for this user then
252  text messages will be sent to it. These may be events or from other
253  participants in the conference bridge. If disabled then no text
254  messages are sent to the user.</para></description>
255  </configOption>
256  <configOption name="answer_channel" default="yes">
257  <synopsis>Sets if a user's channel should be answered if currently unanswered.</synopsis>
258  </configOption>
259  </configObject>
260  <configObject name="bridge_profile">
261  <synopsis>A named profile to apply to specific bridges.</synopsis>
262  <description><para>ConfBridge bridges have a profile associated with them
263  that determine their options. A configuration section is determined to be a
264  <literal>bridge_profile</literal> when the <literal>type</literal> parameter has a value
265  of <literal>bridge</literal>.
266  </para></description>
267  <configOption name="type">
268  <synopsis>Define this configuration category as a bridge profile</synopsis>
269  <description><para>The type parameter determines how a context in the
270  configuration file is interpreted.</para>
271  <enumlist>
272  <enum name="user"><para>Configure the context as a <replaceable>user_profile</replaceable></para></enum>
273  <enum name="bridge"><para>Configure the context as a <replaceable>bridge_profile</replaceable></para></enum>
274  <enum name="menu"><para>Configure the context as a <replaceable>menu</replaceable></para></enum>
275  </enumlist>
276  </description>
277  </configOption>
278  <configOption name="jitterbuffer">
279  <synopsis>Place a jitter buffer on the conference's audio stream</synopsis>
280  </configOption>
281  <configOption name="internal_sample_rate">
282  <synopsis>Set the internal native sample rate for mixing the conference</synopsis>
283  <description><para>
284  Sets the internal native sample rate the
285  conference is mixed at. This is set to automatically
286  adjust the sample rate to the best quality by default.
287  Other values can be anything from 8000-192000. If a
288  sample rate is set that Asterisk does not support, the
289  closest sample rate Asterisk does support to the one requested
290  will be used.
291  </para></description>
292  </configOption>
293  <configOption name="maximum_sample_rate">
294  <synopsis>Set the maximum native sample rate for mixing the conference</synopsis>
295  <description><para>
296  Sets the maximum native sample rate the
297  conference is mixed at. This is set to not have a
298  maximum by default. If a sample rate is specified,
299  though, the native sample rate will never exceed it.
300  </para></description>
301  </configOption>
302  <configOption name="language" default="en">
303  <synopsis>The language used for announcements to the conference.</synopsis>
304  <description><para>
305  By default, announcements to a conference use English. Which means
306  the prompts played to all users within the conference will be
307  English. By changing the language of a bridge, this will change
308  the language of the prompts played to all users.
309  </para></description>
310  </configOption>
311  <configOption name="mixing_interval">
312  <synopsis>Sets the internal mixing interval in milliseconds for the bridge</synopsis>
313  <description><para>
314  Sets the internal mixing interval in milliseconds for the bridge. This
315  number reflects how tight or loose the mixing will be for the conference.
316  In order to improve performance a larger mixing interval such as 40ms may
317  be chosen. Using a larger mixing interval comes at the cost of introducing
318  larger amounts of delay into the bridge. Valid values here are 10, 20, 40,
319  or 80.
320  </para></description>
321  </configOption>
322  <configOption name="binaural_active">
323  <synopsis>If true binaural conferencing with stereo audio is active</synopsis>
324  <description><para>
325  Activates binaural mixing for a conference bridge.
326  Binaural features are disabled by default.
327  </para></description>
328  </configOption>
329  <configOption name="record_conference">
330  <synopsis>Record the conference starting with the first active user's entrance and ending with the last active user's exit</synopsis>
331  <description><para>
332  Records the conference call starting when the first user
333  enters the room, and ending when the last user exits the room.
334  The default recorded filename is
335  <filename>'confbridge-${name of conference bridge}-${start time}.wav'</filename>
336  and the default format is 8khz slinear. This file will be
337  located in the configured monitoring directory in <filename>asterisk.conf</filename>.
338  </para></description>
339  </configOption>
340  <configOption name="record_file" default="confbridge-${name of conference bridge}-${start time}.wav">
341  <synopsis>The filename of the conference recording</synopsis>
342  <description><para>
343  When <replaceable>record_conference</replaceable> is set to yes, the specific name of the
344  record file can be set using this option. Note that since multiple
345  conferences may use the same bridge profile, this may cause issues
346  depending on the configuration. It is recommended to only use this
347  option dynamically with the <literal>CONFBRIDGE()</literal> dialplan function. This
348  allows the record name to be specified and a unique name to be chosen.
349  By default, the record_file is stored in Asterisk's spool/monitor directory
350  with a unique filename starting with the 'confbridge' prefix.
351  </para></description>
352  </configOption>
353  <configOption name="record_file_append" default="yes">
354  <synopsis>Append to record file when starting/stopping on same conference recording</synopsis>
355  <description><para>
356  When <replaceable>record_file_append</replaceable> is set to yes, stopping and starting recording on a
357  conference adds the new portion to end of current record_file. When this is
358  set to no, a new <replaceable>record_file</replaceable> is generated every time you start then stop recording
359  on a conference.
360  </para></description>
361  </configOption>
362  <configOption name="record_file_timestamp" default="yes">
363  <synopsis>Append the start time to the record_file name so that it is unique.</synopsis>
364  <description><para>
365  When <replaceable>record_file_timestamp</replaceable> is set to yes, the start time is appended to
366  <replaceable>record_file</replaceable> so that the filename is unique. This allows you to specify
367  a <replaceable>record_file</replaceable> but not overwrite existing recordings.
368  </para></description>
369  </configOption>
370  <configOption name="record_options" default="">
371  <synopsis>Pass additional options to MixMonitor when recording</synopsis>
372  <description><para>
373  Pass additional options to MixMonitor when <replaceable>record_conference</replaceable> is set to yes.
374  See <literal>MixMonitor</literal> for available options.
375  </para></description>
376  </configOption>
377  <configOption name="record_command" default="">
378  <synopsis>Execute a command after recording ends</synopsis>
379  <description><para>
380  Executes the specified command when recording ends. Any strings matching <literal>^{X}</literal> will be
381  unescaped to <variable>X</variable>. All variables will be evaluated at the time ConfBridge is called.
382  </para></description>
383  </configOption>
384  <configOption name="regcontext">
385  <synopsis>The name of the context into which to register the name of the conference bridge as NoOP() at priority 1</synopsis>
386  <description><para>
387  When set this will cause the name of the created conference to be registered
388  into the named context at priority 1 with an operation of NoOP(). This can
389  then be used in other parts of the dialplan to test for the existence of a
390  specific conference bridge.
391  You should be aware that there are potential races between testing for the
392  existence of a bridge, and taking action upon that information, consider
393  for example two callers executing the check simultaneously, and then taking
394  special action as "first caller" into the bridge. The same for exiting,
395  directly after the check the bridge can be destroyed before the new caller
396  enters (creating a new bridge), for example, and the "first member" actions
397  could thus be missed.
398  </para></description>
399  </configOption>
400  <configOption name="video_mode">
401  <synopsis>Sets how confbridge handles video distribution to the conference participants</synopsis>
402  <description><para>
403  Sets how confbridge handles video distribution to the conference participants.
404  Note that participants wanting to view and be the source of a video feed
405  <emphasis>MUST</emphasis> be sharing the same video codec. Also, using video in conjunction with
406  with the jitterbuffer currently results in the audio being slightly out of sync
407  with the video. This is a result of the jitterbuffer only working on the audio
408  stream. It is recommended to disable the jitterbuffer when video is used.</para>
409  <enumlist>
410  <enum name="none">
411  <para>No video sources are set by default in the conference. It is still
412  possible for a user to be set as a video source via AMI or DTMF action
413  at any time.</para>
414  </enum>
415  <enum name="follow_talker">
416  <para>The video feed will follow whoever is talking and providing video.</para>
417  </enum>
418  <enum name="last_marked">
419  <para>The last marked user to join the conference with video capabilities
420  will be the single source of video distributed to all participants.
421  If multiple marked users are capable of video, the last one to join
422  is always the source, when that user leaves it goes to the one who
423  joined before them.</para>
424  </enum>
425  <enum name="first_marked">
426  <para>The first marked user to join the conference with video capabilities
427  is the single source of video distribution among all participants. If
428  that user leaves, the marked user to join after them becomes the source.</para>
429  </enum>
430  <enum name="sfu">
431  <para>Selective Forwarding Unit - Sets multi-stream
432  operation for a multi-party video conference.</para>
433  </enum>
434  </enumlist>
435  </description>
436  </configOption>
437  <configOption name="max_members">
438  <synopsis>Limit the maximum number of participants for a single conference</synopsis>
439  <description><para>
440  This option limits the number of participants for a single
441  conference to a specific number. By default conferences
442  have no participant limit. After the limit is reached, the
443  conference will be locked until someone leaves. Note however
444  that an Admin user will always be alowed to join the conference
445  regardless if this limit is reached or not.
446  </para></description>
447  </configOption>
448  <configOption name="sound_">
449  <synopsis>Override the various conference bridge sound files</synopsis>
450  <description><para>
451  All sounds in the conference are customizable using the bridge profile options below.
452  Simply state the option followed by the filename or full path of the filename after
453  the option. Example: <literal>sound_had_joined=conf-hasjoin</literal> This will play the <literal>conf-hasjoin</literal>
454  sound file found in the sounds directory when announcing someone's name is joining the
455  conference.</para>
456  <enumlist>
457  <enum name="sound_join"><para>The sound played to everyone when someone enters the conference.</para></enum>
458  <enum name="sound_leave"><para>The sound played to everyone when someone leaves the conference.</para></enum>
459  <enum name="sound_has_joined"><para>The sound played before announcing someone's name has
460  joined the conference. This is used for user intros.
461  Example <literal>"_____ has joined the conference"</literal></para></enum>
462  <enum name="sound_has_left"><para>The sound played when announcing someone's name has
463  left the conference. This is used for user intros.
464  Example <literal>"_____ has left the conference"</literal></para></enum>
465  <enum name="sound_kicked"><para>The sound played to a user who has been kicked from the conference.</para></enum>
466  <enum name="sound_muted"><para>The sound played when the mute option it toggled on.</para></enum>
467  <enum name="sound_unmuted"><para>The sound played when the mute option it toggled off.</para></enum>
468  <enum name="sound_binaural_on"><para>The sound played when binaural audio is turned on.</para></enum>
469  <enum name="sound_binaural_off"><para>The sound played when the binaural audio is turned off.</para></enum>
470  <enum name="sound_only_person"><para>The sound played when the user is the only person in the conference.</para></enum>
471  <enum name="sound_only_one"><para>The sound played to a user when there is only one other
472  person is in the conference.</para></enum>
473  <enum name="sound_there_are"><para>The sound played when announcing how many users there
474  are in a conference.</para></enum>
475  <enum name="sound_other_in_party"><para>This file is used in conjunction with <literal>sound_there_are</literal>
476  when announcing how many users there are in the conference.
477  The sounds are stringed together like this.
478  <literal>"sound_there_are" ${number of participants} "sound_other_in_party"</literal></para></enum>
479  <enum name="sound_place_into_conference"><para>The sound played when someone is placed into the conference
480  after waiting for a marked user.</para></enum>
481  <enum name="sound_wait_for_leader"><para>The sound played when a user is placed into a conference that
482  can not start until a marked user enters.</para></enum>
483  <enum name="sound_leader_has_left"><para>The sound played when the last marked user leaves the conference.</para></enum>
484  <enum name="sound_get_pin"><para>The sound played when prompting for a conference pin number.</para></enum>
485  <enum name="sound_invalid_pin"><para>The sound played when an invalid pin is entered too many times.</para></enum>
486  <enum name="sound_locked"><para>The sound played to a user trying to join a locked conference.</para></enum>
487  <enum name="sound_locked_now"><para>The sound played to an admin after toggling the conference to locked mode.</para></enum>
488  <enum name="sound_unlocked_now"><para>The sound played to an admin after toggling the conference to unlocked mode.</para></enum>
489  <enum name="sound_error_menu"><para>The sound played when an invalid menu option is entered.</para></enum>
490  </enumlist>
491  </description>
492  </configOption>
493  <configOption name="video_update_discard" default="2000">
494  <synopsis>Sets the amount of time in milliseconds after sending a video update to discard subsequent video updates</synopsis>
495  <description><para>
496  Sets the amount of time in milliseconds after sending a video update request
497  that subsequent video updates should be discarded. This means that if we
498  send a video update we will discard any other video update requests until
499  after the configured amount of time has elapsed. This prevents flooding of
500  video update requests from clients.
501  </para></description>
502  </configOption>
503  <configOption name="remb_send_interval" default="0">
504  <synopsis>Sets the interval in milliseconds that a combined REMB frame will be sent to video sources</synopsis>
505  <description><para>
506  Sets the interval in milliseconds that a combined REMB frame will be sent
507  to video sources. This is done by taking all REMB frames that have been
508  received since the last REMB frame was sent, making a combined value,
509  and sending it to the source. A REMB frame contains receiver estimated
510  maximum bitrate information. By creating a combined REMB frame the
511  sender of video can be influenced on the bitrate they choose, allowing
512  better quality for all receivers.
513  </para></description>
514  </configOption>
515  <configOption name="remb_behavior" default="average">
516  <synopsis>Sets how REMB reports are generated from multiple sources</synopsis>
517  <description><para>
518  Sets how REMB reports are combined from multiple sources to form one. A REMB report
519  consists of information about the receiver estimated maximum bitrate. As a source
520  stream may be forwarded to multiple receivers the reports must be combined into
521  a single one which is sent to the sender.</para>
522  <enumlist>
523  <enum name="average">
524  <para>The average of all estimated maximum bitrates is taken and sent
525  to the sender.</para>
526  </enum>
527  <enum name="lowest">
528  <para>The lowest estimated maximum bitrate is forwarded to the sender.</para>
529  </enum>
530  <enum name="highest">
531  <para>The highest estimated maximum bitrate is forwarded to the sender.</para>
532  </enum>
533  <enum name="average_all">
534  <para>The average of all estimated maximum bitrates is taken from all
535  receivers in the bridge and a single value is sent to each sender.</para>
536  </enum>
537  <enum name="lowest_all">
538  <para>The lowest estimated maximum bitrate of all receivers in the bridge
539  is taken and sent to each sender.</para>
540  </enum>
541  <enum name="highest_all">
542  <para>The highest estimated maximum bitrate of all receivers in the bridge
543  is taken and sent to each sender.</para>
544  </enum>
545  <enum name="force">
546  <para>The bitrate configured in <literal>remb_estimated_bitrate</literal>
547  is sent to each sender.</para>
548  </enum>
549  </enumlist>
550  </description>
551  <see-also><ref type="configOption">remb_estimated_bitrate</ref></see-also>
552  </configOption>
553  <configOption name="remb_estimated_bitrate">
554  <synopsis>Sets the estimated bitrate sent to each participant in REMB reports</synopsis>
555  <description><para>
556  When <literal>remb_behavior</literal> is set to <literal>force</literal>,
557  this options sets the estimated bitrate (in bits per second) sent to each participant
558  in REMB reports.
559  </para></description>
560  <see-also><ref type="configOption">remb_behavior</ref></see-also>
561  </configOption>
562  <configOption name="enable_events" default="no">
563  <synopsis>Enables events for this bridge</synopsis>
564  <description><para>
565  If enabled, recipients who joined the bridge via a channel driver
566  that supports Enhanced Messaging (currently only chan_pjsip) will
567  receive in-dialog messages containing a JSON body describing the
568  event. The Content-Type header will be
569  <literal>text/x-ast-confbridge-event</literal>.
570  This feature must also be enabled in user profiles.</para>
571  </description>
572  </configOption>
573  <configOption name="template">
574  <synopsis>When using the CONFBRIDGE dialplan function, use a bridge profile as a template for creating a new temporary profile</synopsis>
575  </configOption>
576  </configObject>
577  <configObject name="menu">
578  <synopsis>A conference user menu</synopsis>
579  <description>
580  <para>Conference users, as defined by a <replaceable>conf_user</replaceable>,
581  can have a DTMF menu assigned to their profile when they enter the
582  <literal>ConfBridge</literal> application.</para>
583  </description>
584  <configOption name="type">
585  <synopsis>Define this configuration category as a menu</synopsis>
586  <description><para>The type parameter determines how a context in the
587  configuration file is interpreted.</para>
588  <enumlist>
589  <enum name="user"><para>Configure the context as a <replaceable>user_profile</replaceable></para></enum>
590  <enum name="bridge"><para>Configure the context as a <replaceable>bridge_profile</replaceable></para></enum>
591  <enum name="menu"><para>Configure the context as a <replaceable>menu</replaceable></para></enum>
592  </enumlist>
593  </description>
594  </configOption>
595  <configOption name="template">
596  <synopsis>When using the CONFBRIDGE dialplan function, use a menu profile as a template for creating a new temporary profile</synopsis>
597  </configOption>
598  <configOption name="^[0-9A-D*#]+$">
599  <synopsis>DTMF sequences to assign various confbridge actions to</synopsis>
600  <description>
601  <para>The ConfBridge application also has the ability to apply custom DTMF menus to
602  each channel using the application. Like the User and Bridge profiles a menu
603  is passed in to ConfBridge as an argument in the dialplan.</para>
604  <para>Below is a list of menu actions that can be assigned to a DTMF sequence.</para>
605  <note><para>
606  To have the first DTMF digit in a sequence be the '#' character, you need to
607  escape it. If it is not escaped then normal config file processing will
608  think it is a directive like #include. For example: The mute setting is
609  toggled when <literal>#1</literal> is pressed.</para>
610  <para><literal>\#1=toggle_mute</literal></para>
611  </note>
612  <note><para>
613  A single DTMF sequence can have multiple actions associated with it. This is
614  accomplished by stringing the actions together and using a <literal>,</literal> as the
615  delimiter. Example: Both listening and talking volume is reset when <literal>5</literal> is
616  pressed. <literal>5=reset_talking_volume, reset_listening_volume</literal></para></note>
617  <enumlist>
618  <enum name="playback(filename&amp;filename2&amp;...)"><para>
619  <literal>playback</literal> will play back an audio file to a channel
620  and then immediately return to the conference.
621  This file can not be interupted by DTMF.
622  Multiple files can be chained together using the
623  <literal>&amp;</literal> character.</para></enum>
624  <enum name="playback_and_continue(filename&amp;filename2&amp;...)"><para>
625  <literal>playback_and_continue</literal> will
626  play back a prompt while continuing to
627  collect the dtmf sequence. This is useful
628  when using a menu prompt that describes all
629  the menu options. Note however that any DTMF
630  during this action will terminate the prompts
631  playback. Prompt files can be chained together
632  using the <literal>&amp;</literal> character as a delimiter.</para></enum>
633  <enum name="toggle_mute"><para>
634  Toggle turning on and off mute. Mute will make the user silent
635  to everyone else, but the user will still be able to listen in.
636  </para></enum>
637  <enum name="toggle_binaural"><para>
638  Toggle turning on and off binaural audio processing.
639  </para></enum>
640  <enum name="no_op"><para>
641  This action does nothing (No Operation). Its only real purpose exists for
642  being able to reserve a sequence in the config as a menu exit sequence.</para></enum>
643  <enum name="decrease_listening_volume"><para>
644  Decreases the channel's listening volume.</para></enum>
645  <enum name="increase_listening_volume"><para>
646  Increases the channel's listening volume.</para></enum>
647  <enum name="reset_listening_volume"><para>
648  Reset channel's listening volume to default level.</para></enum>
649  <enum name="decrease_talking_volume"><para>
650  Decreases the channel's talking volume.</para></enum>
651  <enum name="increase_talking_volume"><para>
652  Increases the channel's talking volume.</para></enum>
653  <enum name="reset_talking_volume"><para>
654  Reset channel's talking volume to default level.</para></enum>
655  <enum name="dialplan_exec(context,exten,priority)"><para>
656  The <literal>dialplan_exec</literal> action allows a user
657  to escape from the conference and execute
658  commands in the dialplan. Once the dialplan
659  exits the user will be put back into the
660  conference. The possibilities are endless!</para></enum>
661  <enum name="leave_conference"><para>
662  This action allows a user to exit the conference and continue
663  execution in the dialplan.</para></enum>
664  <enum name="admin_kick_last"><para>
665  This action allows an Admin to kick the last participant from the
666  conference. This action will only work for admins which allows
667  a single menu to be used for both users and admins.</para></enum>
668  <enum name="admin_toggle_conference_lock"><para>
669  This action allows an Admin to toggle locking and
670  unlocking the conference. Non admins can not use
671  this action even if it is in their menu.</para></enum>
672  <enum name="set_as_single_video_src"><para>
673  This action allows any user to set themselves as the
674  single video source distributed to all participants.
675  This will make the video feed stick to them regardless
676  of what the <literal>video_mode</literal> is set to.</para></enum>
677  <enum name="release_as_single_video_src"><para>
678  This action allows a user to release themselves as
679  the video source. If <literal>video_mode</literal> is not set to <literal>none</literal>
680  this action will result in the conference returning to
681  whatever video mode the bridge profile is using.</para>
682  <para>Note that this action will have no effect if the user
683  is not currently the video source. Also, the user is
684  not guaranteed by using this action that they will not
685  become the video source again. The bridge will return
686  to whatever operation the <literal>video_mode</literal> option is set to
687  upon release of the video src.</para></enum>
688  <enum name="admin_toggle_mute_participants"><para>
689  This action allows an administrator to toggle the mute
690  state for all non-admins within a conference. All
691  admin users are unaffected by this option. Note that all
692  users, regardless of their admin status, are notified
693  that the conference is muted.</para></enum>
694  <enum name="participant_count"><para>
695  This action plays back the number of participants currently
696  in a conference</para></enum>
697  </enumlist>
698  </description>
699  </configOption>
700  </configObject>
701  </configFile>
702  </configInfo>
703 ***/
704 
706  struct ao2_container *bridge_profiles;
707  struct ao2_container *user_profiles;
708  struct ao2_container *menus;
709 };
710 
711 static int verify_default_profiles(void);
712 static void *bridge_profile_alloc(const char *category);
713 static void *bridge_profile_find(struct ao2_container *container, const char *category);
714 static struct bridge_profile_sounds *bridge_profile_sounds_alloc(void);
715 
716 static void bridge_profile_destructor(void *obj)
717 {
718  struct bridge_profile *b_profile = obj;
719  ao2_cleanup(b_profile->sounds);
720 }
721 
722 static void *bridge_profile_alloc(const char *category)
723 {
724  struct bridge_profile *b_profile;
725 
726  if (!(b_profile = ao2_alloc(sizeof(*b_profile), bridge_profile_destructor))) {
727  return NULL;
728  }
729 
730  if (!(b_profile->sounds = bridge_profile_sounds_alloc())) {
731  ao2_ref(b_profile, -1);
732  return NULL;
733  }
734 
735  ast_copy_string(b_profile->name, category, sizeof(b_profile->name));
736 
737  return b_profile;
738 }
739 
740 static void *bridge_profile_find(struct ao2_container *container, const char *category)
741 {
742  return ao2_find(container, category, OBJ_KEY);
743 }
744 
745 static struct aco_type bridge_type = {
746  .type = ACO_ITEM,
747  .name = "bridge_profile",
748  .category_match = ACO_BLACKLIST_EXACT,
749  .category = "general",
750  .matchfield = "type",
751  .matchvalue = "bridge",
752  .item_alloc = bridge_profile_alloc,
753  .item_find = bridge_profile_find,
754  .item_offset = offsetof(struct confbridge_cfg, bridge_profiles),
755 };
756 
757 static void *user_profile_alloc(const char *category);
758 static void *user_profile_find(struct ao2_container *container, const char *category);
759 static void user_profile_destructor(void *obj)
760 {
761  return;
762 }
763 
764 static void *user_profile_alloc(const char *category)
765 {
766  struct user_profile *u_profile;
767 
768  if (!(u_profile = ao2_alloc(sizeof(*u_profile), user_profile_destructor))) {
769  return NULL;
770  }
771 
772  ast_copy_string(u_profile->name, category, sizeof(u_profile->name));
773 
774  return u_profile;
775 }
776 
777 static void *user_profile_find(struct ao2_container *container, const char *category)
778 {
779  return ao2_find(container, category, OBJ_KEY);
780 }
781 
782 static struct aco_type user_type = {
783  .type = ACO_ITEM,
784  .name = "user_profile",
785  .category_match = ACO_BLACKLIST_EXACT,
786  .category = "general",
787  .matchfield = "type",
788  .matchvalue = "user",
789  .item_alloc = user_profile_alloc,
790  .item_find = user_profile_find,
791  .item_offset = offsetof(struct confbridge_cfg, user_profiles),
792 };
793 
794 static void *menu_alloc(const char *category);
795 static void *menu_find(struct ao2_container *container, const char *category);
796 static void menu_destructor(void *obj);
797 
798 static void *menu_alloc(const char *category)
799 {
800  struct conf_menu *menu;
801  if (!(menu = ao2_alloc(sizeof(*menu), menu_destructor))) {
802  return NULL;
803  }
804  ast_copy_string(menu->name, category, sizeof(menu->name));
805  return menu;
806 }
807 
808 static void *menu_find(struct ao2_container *container, const char *category)
809 {
810  return ao2_find(container, category, OBJ_KEY);
811 }
812 
813 static struct aco_type menu_type = {
814  .type = ACO_ITEM,
815  .name = "menu",
816  .category_match = ACO_BLACKLIST_EXACT,
817  .category = "general",
818  .matchfield = "type",
819  .matchvalue = "menu",
820  .item_alloc = menu_alloc,
821  .item_find = menu_find,
822  .item_offset = offsetof(struct confbridge_cfg, menus),
823 };
824 
825 /* Used to pass to aco_option_register */
826 static struct aco_type *bridge_types[] = ACO_TYPES(&bridge_type);
827 static struct aco_type *menu_types[] = ACO_TYPES(&menu_type);
828 static struct aco_type *user_types[] = ACO_TYPES(&user_type);
829 
830 /* The general category is reserved, but unused */
831 static struct aco_type general_type = {
832  .type = ACO_GLOBAL,
833  .name = "global",
834  .category_match = ACO_WHITELIST_EXACT,
835  .category = "general",
836 };
837 
838 static struct aco_file confbridge_conf = {
839  .filename = "confbridge.conf",
840  .types = ACO_TYPES(&bridge_type, &user_type, &menu_type, &general_type),
841 };
842 
843 static AO2_GLOBAL_OBJ_STATIC(cfg_handle);
844 
845 static void *confbridge_cfg_alloc(void);
846 
847 CONFIG_INFO_STANDARD(cfg_info, cfg_handle, confbridge_cfg_alloc,
848  .files = ACO_FILES(&confbridge_conf),
849  .pre_apply_config = verify_default_profiles,
850 );
851 
852 /*! bridge profile container functions */
853 static int bridge_cmp_cb(void *obj, void *arg, int flags)
854 {
855  const struct bridge_profile *left = obj;
856  const struct bridge_profile *right = arg;
857  const char *right_name = arg;
858  int cmp;
859 
860  switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
861  default:
862  case OBJ_POINTER:
863  right_name = right->name;
864  /* Fall through */
865  case OBJ_KEY:
866  cmp = strcasecmp(left->name, right_name);
867  break;
868  case OBJ_PARTIAL_KEY:
869  cmp = strncasecmp(left->name, right_name, strlen(right_name));
870  break;
871  }
872  return cmp ? 0 : CMP_MATCH;
873 }
874 
875 static int bridge_hash_cb(const void *obj, const int flags)
876 {
877  const struct bridge_profile *b_profile = obj;
878  const char *name = obj;
879  int hash;
880 
881  switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
882  default:
883  case OBJ_POINTER:
884  name = b_profile->name;
885  /* Fall through */
886  case OBJ_KEY:
887  hash = ast_str_case_hash(name);
888  break;
889  case OBJ_PARTIAL_KEY:
890  /* Should never happen in hash callback. */
891  ast_assert(0);
892  hash = 0;
893  break;
894  }
895  return hash;
896 }
897 
898 /*! menu container functions */
899 static int menu_cmp_cb(void *obj, void *arg, int flags)
900 {
901  const struct conf_menu *left = obj;
902  const struct conf_menu *right = arg;
903  const char *right_name = arg;
904  int cmp;
905 
906  switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
907  default:
908  case OBJ_POINTER:
909  right_name = right->name;
910  /* Fall through */
911  case OBJ_KEY:
912  cmp = strcasecmp(left->name, right_name);
913  break;
914  case OBJ_PARTIAL_KEY:
915  cmp = strncasecmp(left->name, right_name, strlen(right_name));
916  break;
917  }
918  return cmp ? 0 : CMP_MATCH;
919 }
920 
921 static int menu_hash_cb(const void *obj, const int flags)
922 {
923  const struct conf_menu *menu = obj;
924  const char *name = obj;
925  int hash;
926 
927  switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
928  default:
929  case OBJ_POINTER:
930  name = menu->name;
931  /* Fall through */
932  case OBJ_KEY:
933  hash = ast_str_case_hash(name);
934  break;
935  case OBJ_PARTIAL_KEY:
936  /* Should never happen in hash callback. */
937  ast_assert(0);
938  hash = 0;
939  break;
940  }
941  return hash;
942 }
943 
944 static void menu_destructor(void *obj)
945 {
946  struct conf_menu *menu = obj;
947  struct conf_menu_entry *entry = NULL;
948 
949  while ((entry = AST_LIST_REMOVE_HEAD(&menu->entries, entry))) {
951  ast_free(entry);
952  }
953 }
954 
955 /*! User profile container functions */
956 static int user_cmp_cb(void *obj, void *arg, int flags)
957 {
958  const struct user_profile *left = obj;
959  const struct user_profile *right = arg;
960  const char *right_name = arg;
961  int cmp;
962 
963  switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
964  default:
965  case OBJ_POINTER:
966  right_name = right->name;
967  /* Fall through */
968  case OBJ_KEY:
969  cmp = strcasecmp(left->name, right_name);
970  break;
971  case OBJ_PARTIAL_KEY:
972  cmp = strncasecmp(left->name, right_name, strlen(right_name));
973  break;
974  }
975  return cmp ? 0 : CMP_MATCH;
976 }
977 
978 static int user_hash_cb(const void *obj, const int flags)
979 {
980  const struct user_profile *u_profile = obj;
981  const char *name = obj;
982  int hash;
983 
984  switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
985  default:
986  case OBJ_POINTER:
987  name = u_profile->name;
988  /* Fall through */
989  case OBJ_KEY:
990  hash = ast_str_case_hash(name);
991  break;
992  case OBJ_PARTIAL_KEY:
993  /* Should never happen in hash callback. */
994  ast_assert(0);
995  hash = 0;
996  break;
997  }
998  return hash;
999 }
1000 
1001 /*! Bridge Profile Sounds functions */
1002 static void bridge_profile_sounds_destroy_cb(void *obj)
1003 {
1004  struct bridge_profile_sounds *sounds = obj;
1006 }
1007 
1008 static struct bridge_profile_sounds *bridge_profile_sounds_alloc(void)
1009 {
1010  struct bridge_profile_sounds *sounds = ao2_alloc(sizeof(*sounds), bridge_profile_sounds_destroy_cb);
1011 
1012  if (!sounds) {
1013  return NULL;
1014  }
1015  if (ast_string_field_init(sounds, 512)) {
1016  ao2_ref(sounds, -1);
1017  return NULL;
1018  }
1019 
1020  return sounds;
1021 }
1022 
1023 static int set_sound(const char *sound_name, const char *sound_file, struct bridge_profile *b_profile)
1024 {
1025  struct bridge_profile_sounds *sounds = b_profile->sounds;
1026  if (ast_strlen_zero(sound_file)) {
1027  return -1;
1028  }
1029 
1030  if (!strcasecmp(sound_name, "sound_only_person")) {
1031  ast_string_field_set(sounds, onlyperson, sound_file);
1032  } else if (!strcasecmp(sound_name, "sound_only_one")) {
1033  ast_string_field_set(sounds, onlyone, sound_file);
1034  } else if (!strcasecmp(sound_name, "sound_has_joined")) {
1035  ast_string_field_set(sounds, hasjoin, sound_file);
1036  } else if (!strcasecmp(sound_name, "sound_has_left")) {
1037  ast_string_field_set(sounds, hasleft, sound_file);
1038  } else if (!strcasecmp(sound_name, "sound_kicked")) {
1039  ast_string_field_set(sounds, kicked, sound_file);
1040  } else if (!strcasecmp(sound_name, "sound_muted")) {
1041  ast_string_field_set(sounds, muted, sound_file);
1042  } else if (!strcasecmp(sound_name, "sound_unmuted")) {
1043  ast_string_field_set(sounds, unmuted, sound_file);
1044  } else if (!strcasecmp(sound_name, "sound_binaural_on")) {
1045  ast_string_field_set(sounds, binauralon, sound_file);
1046  } else if (!strcasecmp(sound_name, "sound_binaural_off")) {
1047  ast_string_field_set(sounds, binauraloff, sound_file);
1048  } else if (!strcasecmp(sound_name, "sound_there_are")) {
1049  ast_string_field_set(sounds, thereare, sound_file);
1050  } else if (!strcasecmp(sound_name, "sound_other_in_party")) {
1051  ast_string_field_set(sounds, otherinparty, sound_file);
1052  } else if (!strcasecmp(sound_name, "sound_place_into_conference")) {
1053  static int deprecation_warning = 1;
1054  if (deprecation_warning) {
1055  ast_log(LOG_WARNING, "sound_place_into_conference is deprecated"
1056  " and unused. Use sound_begin for similar functionality.");
1057  deprecation_warning = 0;
1058  }
1059  ast_string_field_set(sounds, placeintoconf, sound_file);
1060  } else if (!strcasecmp(sound_name, "sound_wait_for_leader")) {
1061  ast_string_field_set(sounds, waitforleader, sound_file);
1062  } else if (!strcasecmp(sound_name, "sound_leader_has_left")) {
1063  ast_string_field_set(sounds, leaderhasleft, sound_file);
1064  } else if (!strcasecmp(sound_name, "sound_get_pin")) {
1065  ast_string_field_set(sounds, getpin, sound_file);
1066  } else if (!strcasecmp(sound_name, "sound_invalid_pin")) {
1067  ast_string_field_set(sounds, invalidpin, sound_file);
1068  } else if (!strcasecmp(sound_name, "sound_locked")) {
1069  ast_string_field_set(sounds, locked, sound_file);
1070  } else if (!strcasecmp(sound_name, "sound_unlocked_now")) {
1071  ast_string_field_set(sounds, unlockednow, sound_file);
1072  } else if (!strcasecmp(sound_name, "sound_locked_now")) {
1073  ast_string_field_set(sounds, lockednow, sound_file);
1074  } else if (!strcasecmp(sound_name, "sound_error_menu")) {
1075  ast_string_field_set(sounds, errormenu, sound_file);
1076  } else if (!strcasecmp(sound_name, "sound_join")) {
1077  ast_string_field_set(sounds, join, sound_file);
1078  } else if (!strcasecmp(sound_name, "sound_leave")) {
1079  ast_string_field_set(sounds, leave, sound_file);
1080  } else if (!strcasecmp(sound_name, "sound_participants_muted")) {
1081  ast_string_field_set(sounds, participantsmuted, sound_file);
1082  } else if (!strcasecmp(sound_name, "sound_participants_unmuted")) {
1083  ast_string_field_set(sounds, participantsunmuted, sound_file);
1084  } else if (!strcasecmp(sound_name, "sound_begin")) {
1085  ast_string_field_set(sounds, begin, sound_file);
1086  } else {
1087  return -1;
1088  }
1089 
1090  return 0;
1091 }
1092 
1093 /*! CONFBRIDGE dialplan function functions and channel datastore. */
1095  struct bridge_profile b_profile;
1096  struct user_profile u_profile;
1097  struct conf_menu *menu;
1098  unsigned int b_usable:1; /*!< Tells if bridge profile is usable or not */
1099  unsigned int u_usable:1; /*!< Tells if user profile is usable or not */
1100  unsigned int m_usable:1; /*!< Tells if menu profile is usable or not */
1101 };
1102 
1103 static void func_confbridge_data_destructor(struct func_confbridge_data *b_data)
1104 {
1105  conf_bridge_profile_destroy(&b_data->b_profile);
1106  ao2_cleanup(b_data->menu);
1107  ast_free(b_data);
1108 }
1109 
1110 static void func_confbridge_destroy_cb(void *data)
1111 {
1112  struct func_confbridge_data *b_data = data;
1113  func_confbridge_data_destructor(b_data);
1114 };
1115 
1116 static const struct ast_datastore_info confbridge_datastore = {
1117  .type = "confbridge",
1118  .destroy = func_confbridge_destroy_cb
1119 };
1120 
1121 int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value)
1122 {
1123  struct ast_datastore *datastore;
1124  struct func_confbridge_data *b_data;
1125  char *parse;
1126  struct ast_variable tmpvar = { 0, };
1127  struct ast_variable template = {
1128  .name = "template",
1129  .file = "CONFBRIDGE"
1130  };
1131  AST_DECLARE_APP_ARGS(args,
1132  AST_APP_ARG(type);
1133  AST_APP_ARG(option);
1134  );
1135 
1136  if (!chan) {
1137  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
1138  return -1;
1139  }
1140 
1141  /* parse all the required arguments and make sure they exist. */
1142  if (ast_strlen_zero(data)) {
1143  return -1;
1144  }
1145  parse = ast_strdupa(data);
1146  AST_STANDARD_APP_ARGS(args, parse);
1147  if (ast_strlen_zero(args.type) || ast_strlen_zero(args.option)) {
1148  return -1;
1149  }
1150 
1151  ast_channel_lock(chan);
1152  datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
1153  if (!datastore) {
1154  datastore = ast_datastore_alloc(&confbridge_datastore, NULL);
1155  if (!datastore) {
1156  ast_channel_unlock(chan);
1157  return 0;
1158  }
1159  b_data = ast_calloc(1, sizeof(*b_data));
1160  if (!b_data) {
1161  ast_channel_unlock(chan);
1162  ast_datastore_free(datastore);
1163  return 0;
1164  }
1165  datastore->data = b_data;
1166  b_data->b_profile.sounds = bridge_profile_sounds_alloc();
1167  if (!b_data->b_profile.sounds) {
1168  ast_channel_unlock(chan);
1169  ast_datastore_free(datastore);
1170  return 0;
1171  }
1172  if (!(b_data->menu = menu_alloc("dialplan"))) {
1173  ast_channel_unlock(chan);
1174  ast_datastore_free(datastore);
1175  return 0;
1176  }
1177  ast_channel_datastore_add(chan, datastore);
1178  } else {
1179  b_data = datastore->data;
1180  }
1181  ast_channel_unlock(chan);
1182 
1183  /* SET(CONFBRIDGE(type,option)=value) */
1184  if (!value) {
1185  value = "";
1186  }
1187  tmpvar.name = args.option;
1188  tmpvar.value = value;
1189  tmpvar.file = "CONFBRIDGE";
1190  if (!strcasecmp(args.type, "bridge")) {
1191  if (!strcasecmp(args.option, "clear")) {
1192  b_data->b_usable = 0;
1193  conf_bridge_profile_destroy(&b_data->b_profile);
1194  memset(&b_data->b_profile, 0, sizeof(b_data->b_profile)) ;
1195  if (!(b_data->b_profile.sounds = bridge_profile_sounds_alloc())) {
1196  /* If this reallocation fails, the datastore has become unusable and must be destroyed. */
1197  ast_channel_lock(chan);
1198  ast_channel_datastore_remove(chan, datastore);
1199  ast_channel_unlock(chan);
1200  ast_datastore_free(datastore);
1201  }
1202  return 0;
1203  }
1204 
1205  if (b_data && !b_data->b_usable && strcasecmp(args.option, "template")) {
1206  template.value = DEFAULT_BRIDGE_PROFILE;
1207  aco_process_var(&bridge_type, "dialplan", &template, &b_data->b_profile);
1208  }
1209 
1210  if (!aco_process_var(&bridge_type, "dialplan", &tmpvar, &b_data->b_profile)) {
1211  b_data->b_usable = 1;
1212  return 0;
1213  }
1214  } else if (!strcasecmp(args.type, "user")) {
1215  if (!strcasecmp(args.option, "clear")) {
1216  b_data->u_usable = 0;
1217  user_profile_destructor(&b_data->u_profile);
1218  memset(&b_data->u_profile, 0, sizeof(b_data->u_profile));
1219  return 0;
1220  }
1221 
1222  if (b_data && !b_data->u_usable && strcasecmp(args.option, "template")) {
1223  template.value = DEFAULT_USER_PROFILE;
1224  aco_process_var(&user_type, "dialplan", &template, &b_data->u_profile);
1225  }
1226 
1227  if (!aco_process_var(&user_type, "dialplan", &tmpvar, &b_data->u_profile)) {
1228  b_data->u_usable = 1;
1229  return 0;
1230  }
1231  } else if (!strcasecmp(args.type, "menu")) {
1232  if (!strcasecmp(args.option, "clear")) {
1233  b_data->m_usable = 0;
1234  ao2_cleanup(b_data->menu);
1235  if (!(b_data->menu = menu_alloc("dialplan"))) {
1236  /* If this reallocation fails, the datastore has become unusable and must be destroyed */
1237  ast_channel_lock(chan);
1238  ast_channel_datastore_remove(chan, datastore);
1239  ast_channel_unlock(chan);
1240  ast_datastore_free(datastore);
1241  }
1242  return 0;
1243  }
1244 
1245  if (b_data && !b_data->m_usable && strcasecmp(args.option, "template")) {
1246  template.value = DEFAULT_MENU_PROFILE;
1247  aco_process_var(&menu_type, "dialplan", &template, &b_data->menu);
1248  }
1249 
1250  if (!aco_process_var(&menu_type, "dialplan", &tmpvar, b_data->menu)) {
1251  b_data->m_usable = 1;
1252  return 0;
1253  }
1254  }
1255 
1256  ast_log(LOG_WARNING, "%s(%s,%s) cannot be set to '%s'. Invalid type, option, or value.\n",
1257  cmd, args.type, args.option, value);
1258  return -1;
1259 }
1260 
1261 static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum conf_menu_action_id id, char *databuf)
1262 {
1263  struct conf_menu_action *menu_action = ast_calloc(1, sizeof(*menu_action));
1264 
1265  if (!menu_action) {
1266  return -1;
1267  }
1268  menu_action->id = id;
1269 
1270  switch (id) {
1271  case MENU_ACTION_NOOP:
1272  case MENU_ACTION_TOGGLE_MUTE:
1273  case MENU_ACTION_TOGGLE_BINAURAL:
1274  case MENU_ACTION_INCREASE_LISTENING:
1275  case MENU_ACTION_DECREASE_LISTENING:
1276  case MENU_ACTION_INCREASE_TALKING:
1277  case MENU_ACTION_DECREASE_TALKING:
1278  case MENU_ACTION_RESET_LISTENING:
1279  case MENU_ACTION_RESET_TALKING:
1280  case MENU_ACTION_ADMIN_TOGGLE_LOCK:
1281  case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
1282  case MENU_ACTION_PARTICIPANT_COUNT:
1283  case MENU_ACTION_ADMIN_KICK_LAST:
1284  case MENU_ACTION_LEAVE:
1285  case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
1286  case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
1287  break;
1288  case MENU_ACTION_PLAYBACK:
1289  case MENU_ACTION_PLAYBACK_AND_CONTINUE:
1290  if (!(ast_strlen_zero(databuf))) {
1291  ast_copy_string(menu_action->data.playback_file, databuf, sizeof(menu_action->data.playback_file));
1292  } else {
1293  ast_free(menu_action);
1294  return -1;
1295  }
1296  break;
1297  case MENU_ACTION_DIALPLAN_EXEC:
1298  if (!(ast_strlen_zero(databuf))) {
1299  AST_DECLARE_APP_ARGS(args,
1300  AST_APP_ARG(context);
1301  AST_APP_ARG(exten);
1302  AST_APP_ARG(priority);
1303  );
1304  AST_STANDARD_APP_ARGS(args, databuf);
1305  if (!ast_strlen_zero(args.context)) {
1306  ast_copy_string(menu_action->data.dialplan_args.context,
1307  args.context,
1308  sizeof(menu_action->data.dialplan_args.context));
1309  }
1310  if (!ast_strlen_zero(args.exten)) {
1311  ast_copy_string(menu_action->data.dialplan_args.exten,
1312  args.exten,
1313  sizeof(menu_action->data.dialplan_args.exten));
1314  }
1315  menu_action->data.dialplan_args.priority = 1; /* 1 by default */
1316  if (!ast_strlen_zero(args.priority) &&
1317  (sscanf(args.priority, "%30d", &menu_action->data.dialplan_args.priority) != 1)) {
1318  /* invalid priority */
1319  ast_free(menu_action);
1320  return -1;
1321  }
1322  } else {
1323  ast_free(menu_action);
1324  return -1;
1325  }
1326  };
1327 
1328  AST_LIST_INSERT_TAIL(&menu_entry->actions, menu_action, action);
1329 
1330  return 0;
1331 }
1332 
1333 static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *action_names)
1334 {
1335  struct conf_menu_entry *menu_entry = NULL, *cur = NULL;
1336  int res = 0;
1337  char *tmp_action_names = ast_strdupa(action_names);
1338  char *action = NULL;
1339  char *action_args;
1340  char *tmp;
1341  char buf[PATH_MAX];
1342  char *delimiter = ",";
1343 
1344  if (!(menu_entry = ast_calloc(1, sizeof(*menu_entry)))) {
1345  return -1;
1346  }
1347 
1348  for (;;) {
1349  char *comma;
1350  char *startbrace;
1351  char *endbrace;
1352  unsigned int action_len;
1353 
1354  if (ast_strlen_zero(tmp_action_names)) {
1355  break;
1356  }
1357  startbrace = strchr(tmp_action_names, '(');
1358  endbrace = strchr(tmp_action_names, ')');
1359  comma = strchr(tmp_action_names, ',');
1360 
1361  /* If the next action has brackets with comma delimited arguments in it,
1362  * make the delimeter ')' instead of a comma to preserve the arguments */
1363  if (startbrace && endbrace && comma && (comma > startbrace && comma < endbrace)) {
1364  delimiter = ")";
1365  } else {
1366  delimiter = ",";
1367  }
1368 
1369  if (!(action = strsep(&tmp_action_names, delimiter))) {
1370  break;
1371  }
1372 
1373  action = ast_strip(action);
1374  if (ast_strlen_zero(action)) {
1375  continue;
1376  }
1377 
1378  action_len = strlen(action);
1379  ast_copy_string(menu_entry->dtmf, dtmf, sizeof(menu_entry->dtmf));
1380  if (!strcasecmp(action, "toggle_mute")) {
1381  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_TOGGLE_MUTE, NULL);
1382  } else if (!strcasecmp(action, "toggle_binaural")) {
1383  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_TOGGLE_BINAURAL, NULL);
1384  } else if (!strcasecmp(action, "no_op")) {
1385  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_NOOP, NULL);
1386  } else if (!strcasecmp(action, "increase_listening_volume")) {
1387  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_LISTENING, NULL);
1388  } else if (!strcasecmp(action, "decrease_listening_volume")) {
1389  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_LISTENING, NULL);
1390  } else if (!strcasecmp(action, "increase_talking_volume")) {
1391  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_TALKING, NULL);
1392  } else if (!strcasecmp(action, "reset_listening_volume")) {
1393  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_LISTENING, NULL);
1394  } else if (!strcasecmp(action, "reset_talking_volume")) {
1395  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_TALKING, NULL);
1396  } else if (!strcasecmp(action, "decrease_talking_volume")) {
1397  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_TALKING, NULL);
1398  } else if (!strcasecmp(action, "admin_toggle_conference_lock")) {
1399  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_LOCK, NULL);
1400  } else if (!strcasecmp(action, "admin_toggle_mute_participants")) {
1401  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS, NULL);
1402  } else if (!strcasecmp(action, "participant_count")) {
1403  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PARTICIPANT_COUNT, NULL);
1404  } else if (!strcasecmp(action, "admin_kick_last")) {
1405  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_KICK_LAST, NULL);
1406  } else if (!strcasecmp(action, "leave_conference")) {
1407  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_LEAVE, NULL);
1408  } else if (!strcasecmp(action, "set_as_single_video_src")) {
1409  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_SET_SINGLE_VIDEO_SRC, NULL);
1410  } else if (!strcasecmp(action, "release_as_single_video_src")) {
1411  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC, NULL);
1412  } else if (!strncasecmp(action, "dialplan_exec(", 14)) {
1413  ast_copy_string(buf, action, sizeof(buf));
1414  action_args = buf;
1415  if ((action_args = strchr(action, '('))) {
1416  action_args++;
1417  }
1418  /* it is possible that this argument may or may not
1419  * have a closing brace at this point, it all depends on if
1420  * comma delimited arguments were provided */
1421  if ((tmp = strchr(action, ')'))) {
1422  *tmp = '\0';
1423  }
1424  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DIALPLAN_EXEC, action_args);
1425  } else if (action_len >= 21 && !strncasecmp(action, "playback_and_continue(", 22)) {
1426  ast_copy_string(buf, action, sizeof(buf));
1427  action_args = buf;
1428  if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
1429  *tmp = '\0';
1430  action_args++;
1431  }
1432  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK_AND_CONTINUE, action_args);
1433  } else if (action_len >= 8 && !strncasecmp(action, "playback(", 9)) {
1434  ast_copy_string(buf, action, sizeof(buf));
1435  action_args = buf;
1436  if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
1437  *tmp = '\0';
1438  action_args++;
1439  }
1440  res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK, action_args);
1441  }
1442  }
1443 
1444  /* if adding any of the actions failed, bail */
1445  if (res) {
1446  conf_menu_entry_destroy(menu_entry);
1447  ast_free(menu_entry);
1448  return -1;
1449  }
1450 
1451  /* remove any list entry with an identical DTMF sequence for overrides */
1452  AST_LIST_TRAVERSE_SAFE_BEGIN(&menu->entries, cur, entry) {
1453  if (!strcasecmp(cur->dtmf, menu_entry->dtmf)) {
1454  AST_LIST_REMOVE_CURRENT(entry);
1456  ast_free(cur);
1457  break;
1458  }
1459  }
1461 
1462  AST_LIST_INSERT_TAIL(&menu->entries, menu_entry, entry);
1463 
1464  return 0;
1465 }
1466 
1467 static char *complete_user_profile_name(const char *line, const char *word, int pos, int state)
1468 {
1469  int which = 0;
1470  char *res = NULL;
1471  int wordlen = strlen(word);
1472  struct ao2_iterator i;
1473  struct user_profile *u_profile = NULL;
1474  RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
1475 
1476  if (!cfg) {
1477  return NULL;
1478  }
1479 
1480  i = ao2_iterator_init(cfg->user_profiles, 0);
1481  while ((u_profile = ao2_iterator_next(&i))) {
1482  if (!strncasecmp(u_profile->name, word, wordlen) && ++which > state) {
1483  res = ast_strdup(u_profile->name);
1484  ao2_ref(u_profile, -1);
1485  break;
1486  }
1487  ao2_ref(u_profile, -1);
1488  }
1490 
1491  return res;
1492 }
1493 
1494 static char *handle_cli_confbridge_show_user_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1495 {
1496  struct ao2_iterator it;
1497  struct user_profile *u_profile;
1498  RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
1499 
1500  switch (cmd) {
1501  case CLI_INIT:
1502  e->command = "confbridge show profile users";
1503  e->usage =
1504  "Usage: confbridge show profile users\n";
1505  return NULL;
1506  case CLI_GENERATE:
1507  return NULL;
1508  }
1509 
1510  if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
1511  return NULL;
1512  }
1513 
1514  ast_cli(a->fd,"--------- User Profiles -----------\n");
1515  ao2_lock(cfg->user_profiles);
1516  it = ao2_iterator_init(cfg->user_profiles, 0);
1517  while ((u_profile = ao2_iterator_next(&it))) {
1518  ast_cli(a->fd,"%s\n", u_profile->name);
1519  ao2_ref(u_profile, -1);
1520  }
1521  ao2_iterator_destroy(&it);
1522  ao2_unlock(cfg->user_profiles);
1523 
1524  return CLI_SUCCESS;
1525 }
1526 static char *handle_cli_confbridge_show_user_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1527 {
1528  struct user_profile u_profile;
1529 
1530  switch (cmd) {
1531  case CLI_INIT:
1532  e->command = "confbridge show profile user";
1533  e->usage =
1534  "Usage: confbridge show profile user [<profile name>]\n";
1535  return NULL;
1536  case CLI_GENERATE:
1537  if (a->pos == 4) {
1538  return complete_user_profile_name(a->line, a->word, a->pos, a->n);
1539  }
1540  return NULL;
1541  }
1542 
1543  if (a->argc != 5) {
1544  return CLI_SHOWUSAGE;
1545  }
1546 
1547  if (!(conf_find_user_profile(NULL, a->argv[4], &u_profile))) {
1548  ast_cli(a->fd, "No conference user profile named '%s' found!\n", a->argv[4]);
1549  return CLI_SUCCESS;
1550  }
1551 
1552  ast_cli(a->fd,"--------------------------------------------\n");
1553  ast_cli(a->fd,"Name: %s\n",
1554  u_profile.name);
1555  ast_cli(a->fd,"Admin: %s\n",
1556  u_profile.flags & USER_OPT_ADMIN ?
1557  "true" : "false");
1558  ast_cli(a->fd,"Send Events: %s\n",
1559  u_profile.flags & USER_OPT_SEND_EVENTS ?
1560  "true" : "false");
1561  ast_cli(a->fd,"Echo Events: %s\n",
1562  u_profile.flags & USER_OPT_ECHO_EVENTS ?
1563  "true" : "false");
1564  ast_cli(a->fd,"Marked User: %s\n",
1565  u_profile.flags & USER_OPT_MARKEDUSER ?
1566  "true" : "false");
1567  ast_cli(a->fd,"Start Muted: %s\n",
1568  u_profile.flags & USER_OPT_STARTMUTED?
1569  "true" : "false");
1570  ast_cli(a->fd,"MOH When Empty: %s\n",
1571  u_profile.flags & USER_OPT_MUSICONHOLD ?
1572  "enabled" : "disabled");
1573  ast_cli(a->fd,"MOH Class: %s\n",
1574  ast_strlen_zero(u_profile.moh_class) ?
1575  "default" : u_profile.moh_class);
1576  ast_cli(a->fd,"Announcement: %s\n",
1577  u_profile.announcement);
1578  ast_cli(a->fd,"Quiet: %s\n",
1579  u_profile.flags & USER_OPT_QUIET ?
1580  "enabled" : "disabled");
1581  ast_cli(a->fd,"Hear Join: %s\n",
1582  u_profile.flags & USER_OPT_HEAR_OWN_JOIN_SOUND ?
1583  "enabled" : "disabled");
1584  ast_cli(a->fd,"Wait Marked: %s\n",
1585  u_profile.flags & USER_OPT_WAITMARKED ?
1586  "enabled" : "disabled");
1587  ast_cli(a->fd,"END Marked (All): %s\n",
1588  u_profile.flags & USER_OPT_ENDMARKED ?
1589  "enabled" : "disabled");
1590  ast_cli(a->fd,"END Marked (Any): %s\n",
1591  u_profile.flags & USER_OPT_ENDMARKEDANY ?
1592  "enabled" : "disabled");
1593  ast_cli(a->fd,"Drop_silence: %s\n",
1594  u_profile.flags & USER_OPT_DROP_SILENCE ?
1595  "enabled" : "disabled");
1596  ast_cli(a->fd,"Silence Threshold: %ums\n",
1597  u_profile.silence_threshold);
1598  ast_cli(a->fd,"Talking Threshold: %u\n",
1599  u_profile.talking_threshold);
1600  ast_cli(a->fd,"Denoise: %s\n",
1601  u_profile.flags & USER_OPT_DENOISE ?
1602  "enabled" : "disabled");
1603  ast_cli(a->fd,"Jitterbuffer: %s\n",
1604  u_profile.flags & USER_OPT_JITTERBUFFER ?
1605  "enabled" : "disabled");
1606  ast_cli(a->fd,"Talk Detect Events: %s\n",
1607  u_profile.flags & USER_OPT_TALKER_DETECT ?
1608  "enabled" : "disabled");
1609  ast_cli(a->fd,"DTMF Pass Through: %s\n",
1610  u_profile.flags & USER_OPT_DTMF_PASS ?
1611  "enabled" : "disabled");
1612  ast_cli(a->fd,"PIN: %s\n",
1613  ast_strlen_zero(u_profile.pin) ?
1614  "None" : u_profile.pin);
1615  ast_cli(a->fd,"Announce User Count: %s\n",
1616  u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNT ?
1617  "enabled" : "disabled");
1618  ast_cli(a->fd,"Announce join/leave: %s\n",
1619  u_profile.flags & (USER_OPT_ANNOUNCE_JOIN_LEAVE | USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW) ?
1620  u_profile.flags & USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW ?
1621  "enabled (with review)" : "enabled" : "disabled");
1622  ast_cli(a->fd,"Announce User Count all: %s\n",
1623  u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNTALL ?
1624  "enabled" : "disabled");
1625  ast_cli(a->fd,"Text Messaging: %s\n",
1626  u_profile.flags & USER_OPT_TEXT_MESSAGING ?
1627  "enabled" : "disabled");
1628  ast_cli(a->fd,"Answer Channel: %s\n",
1629  u_profile.flags & USER_OPT_ANSWER_CHANNEL ?
1630  "true" : "false");
1631  ast_cli(a->fd, "\n");
1632 
1633  return CLI_SUCCESS;
1634 }
1635 
1636 static char *complete_bridge_profile_name(const char *line, const char *word, int pos, int state)
1637 {
1638  int which = 0;
1639  char *res = NULL;
1640  int wordlen = strlen(word);
1641  struct ao2_iterator i;
1642  struct bridge_profile *b_profile = NULL;
1643  RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
1644 
1645  if (!cfg) {
1646  return NULL;
1647  }
1648 
1649  i = ao2_iterator_init(cfg->bridge_profiles, 0);
1650  while ((b_profile = ao2_iterator_next(&i))) {
1651  if (!strncasecmp(b_profile->name, word, wordlen) && ++which > state) {
1652  res = ast_strdup(b_profile->name);
1653  ao2_ref(b_profile, -1);
1654  break;
1655  }
1656  ao2_ref(b_profile, -1);
1657  }
1659 
1660  return res;
1661 }
1662 
1663 static char *handle_cli_confbridge_show_bridge_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1664 {
1665  struct ao2_iterator it;
1666  struct bridge_profile *b_profile;
1667  RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
1668 
1669  switch (cmd) {
1670  case CLI_INIT:
1671  e->command = "confbridge show profile bridges";
1672  e->usage =
1673  "Usage: confbridge show profile bridges\n";
1674  return NULL;
1675  case CLI_GENERATE:
1676  return NULL;
1677  }
1678 
1679  if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
1680  return NULL;
1681  }
1682 
1683  ast_cli(a->fd,"--------- Bridge Profiles -----------\n");
1684  ao2_lock(cfg->bridge_profiles);
1685  it = ao2_iterator_init(cfg->bridge_profiles, 0);
1686  while ((b_profile = ao2_iterator_next(&it))) {
1687  ast_cli(a->fd,"%s\n", b_profile->name);
1688  ao2_ref(b_profile, -1);
1689  }
1690  ao2_iterator_destroy(&it);
1691  ao2_unlock(cfg->bridge_profiles);
1692 
1693  return CLI_SUCCESS;
1694 }
1695 
1696 static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1697 {
1698  struct bridge_profile b_profile;
1699  char tmp[64];
1700 
1701  switch (cmd) {
1702  case CLI_INIT:
1703  e->command = "confbridge show profile bridge";
1704  e->usage =
1705  "Usage: confbridge show profile bridge <profile name>\n";
1706  return NULL;
1707  case CLI_GENERATE:
1708  if (a->pos == 4) {
1709  return complete_bridge_profile_name(a->line, a->word, a->pos, a->n);
1710  }
1711  return NULL;
1712  }
1713 
1714  if (a->argc != 5) {
1715  return CLI_SHOWUSAGE;
1716  }
1717 
1718  if (!(conf_find_bridge_profile(NULL, a->argv[4], &b_profile))) {
1719  ast_cli(a->fd, "No conference bridge profile named '%s' found!\n", a->argv[4]);
1720  return CLI_SUCCESS;
1721  }
1722 
1723  ast_cli(a->fd,"--------------------------------------------\n");
1724  ast_cli(a->fd,"Name: %s\n", b_profile.name);
1725  ast_cli(a->fd,"Language: %s\n", b_profile.language);
1726 
1727  if (b_profile.internal_sample_rate) {
1728  snprintf(tmp, sizeof(tmp), "%u", b_profile.internal_sample_rate);
1729  } else {
1730  ast_copy_string(tmp, "auto", sizeof(tmp));
1731  }
1732  ast_cli(a->fd,"Internal Sample Rate: %s\n", tmp);
1733 
1734  if (b_profile.maximum_sample_rate) {
1735  snprintf(tmp, sizeof(tmp), "%u", b_profile.maximum_sample_rate);
1736  } else {
1737  ast_copy_string(tmp, "none", sizeof(tmp));
1738  }
1739  ast_cli(a->fd,"Maximum Sample Rate: %s\n", tmp);
1740 
1741  if (b_profile.mix_interval) {
1742  ast_cli(a->fd,"Mixing Interval: %u\n", b_profile.mix_interval);
1743  } else {
1744  ast_cli(a->fd,"Mixing Interval: Default 20ms\n");
1745  }
1746 
1747  ast_cli(a->fd,"Record Conference: %s\n",
1748  b_profile.flags & BRIDGE_OPT_RECORD_CONFERENCE ?
1749  "yes" : "no");
1750 
1751  ast_cli(a->fd,"Record File Append: %s\n",
1752  b_profile.flags & BRIDGE_OPT_RECORD_FILE_APPEND ?
1753  "yes" : "no");
1754 
1755  ast_cli(a->fd,"Record File Timestamp: %s\n",
1756  b_profile.flags & BRIDGE_OPT_RECORD_FILE_TIMESTAMP ?
1757  "yes" : "no");
1758 
1759  ast_cli(a->fd,"Record File: %s\n",
1760  ast_strlen_zero(b_profile.rec_file) ? "Auto Generated" :
1761  b_profile.rec_file);
1762 
1763  ast_cli(a->fd,"Record Options: %s\n",
1764  b_profile.rec_options);
1765 
1766  ast_cli(a->fd,"Record Command: %s\n",
1767  b_profile.rec_command);
1768 
1769  if (b_profile.max_members) {
1770  ast_cli(a->fd,"Max Members: %u\n", b_profile.max_members);
1771  } else {
1772  ast_cli(a->fd,"Max Members: No Limit\n");
1773  }
1774 
1775  ast_cli(a->fd,"Registration context: %s\n", b_profile.regcontext);
1776 
1777  switch (b_profile.flags
1778  & (BRIDGE_OPT_VIDEO_SRC_LAST_MARKED |
1779  BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED |
1780  BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER |
1781  BRIDGE_OPT_VIDEO_SRC_SFU)) {
1782  case BRIDGE_OPT_VIDEO_SRC_LAST_MARKED:
1783  ast_cli(a->fd, "Video Mode: last_marked\n");
1784  break;
1785  case BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED:
1786  ast_cli(a->fd, "Video Mode: first_marked\n");
1787  break;
1788  case BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER:
1789  ast_cli(a->fd, "Video Mode: follow_talker\n");
1790  break;
1791  case BRIDGE_OPT_VIDEO_SRC_SFU:
1792  ast_cli(a->fd, "Video Mode: sfu\n");
1793  break;
1794  case 0:
1795  ast_cli(a->fd, "Video Mode: no video\n");
1796  break;
1797  default:
1798  /* Opps. We have more than one video mode flag set. */
1799  ast_assert(0);
1800  break;
1801  }
1802 
1803  ast_cli(a->fd,"Video Update Discard: %u\n", b_profile.video_update_discard);
1804  ast_cli(a->fd,"REMB Send Interval: %u\n", b_profile.remb_send_interval);
1805 
1806  switch (b_profile.flags
1807  & (BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE | BRIDGE_OPT_REMB_BEHAVIOR_LOWEST
1808  | BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST | BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE_ALL
1809  | BRIDGE_OPT_REMB_BEHAVIOR_LOWEST_ALL | BRIDGE_OPT_REMB_BEHAVIOR_LOWEST_ALL)) {
1810  case BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE:
1811  ast_cli(a->fd, "REMB Behavior: average\n");
1812  break;
1813  case BRIDGE_OPT_REMB_BEHAVIOR_LOWEST:
1814  ast_cli(a->fd, "REMB Behavior: lowest\n");
1815  break;
1816  case BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST:
1817  ast_cli(a->fd, "REMB Behavior: highest\n");
1818  break;
1819  case BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE_ALL:
1820  ast_cli(a->fd, "REMB Behavior: average_all\n");
1821  break;
1822  case BRIDGE_OPT_REMB_BEHAVIOR_LOWEST_ALL:
1823  ast_cli(a->fd, "REMB Behavior: lowest_all\n");
1824  break;
1825  case BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST_ALL:
1826  ast_cli(a->fd, "REMB Behavior: highest_all\n");
1827  break;
1828  default:
1829  ast_assert(0);
1830  break;
1831  }
1832 
1833  ast_cli(a->fd,"Enable Events: %s\n",
1834  b_profile.flags & BRIDGE_OPT_ENABLE_EVENTS ?
1835  "yes" : "no");
1836 
1837  ast_cli(a->fd,"sound_only_person: %s\n", conf_get_sound(CONF_SOUND_ONLY_PERSON, b_profile.sounds));
1838  ast_cli(a->fd,"sound_only_one: %s\n", conf_get_sound(CONF_SOUND_ONLY_ONE, b_profile.sounds));
1839  ast_cli(a->fd,"sound_has_joined: %s\n", conf_get_sound(CONF_SOUND_HAS_JOINED, b_profile.sounds));
1840  ast_cli(a->fd,"sound_has_left: %s\n", conf_get_sound(CONF_SOUND_HAS_LEFT, b_profile.sounds));
1841  ast_cli(a->fd,"sound_kicked: %s\n", conf_get_sound(CONF_SOUND_KICKED, b_profile.sounds));
1842  ast_cli(a->fd,"sound_muted: %s\n", conf_get_sound(CONF_SOUND_MUTED, b_profile.sounds));
1843  ast_cli(a->fd,"sound_unmuted: %s\n", conf_get_sound(CONF_SOUND_UNMUTED, b_profile.sounds));
1844  ast_cli(a->fd,"sound_binaural_on: %s\n", conf_get_sound(CONF_SOUND_BINAURAL_ON, b_profile.sounds));
1845  ast_cli(a->fd,"sound_binaural_off: %s\n", conf_get_sound(CONF_SOUND_BINAURAL_OFF, b_profile.sounds));
1846  ast_cli(a->fd,"sound_there_are: %s\n", conf_get_sound(CONF_SOUND_THERE_ARE, b_profile.sounds));
1847  ast_cli(a->fd,"sound_other_in_party: %s\n", conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, b_profile.sounds));
1848  ast_cli(a->fd,"sound_place_into_conference: %s\n", conf_get_sound(CONF_SOUND_PLACE_IN_CONF, b_profile.sounds));
1849  ast_cli(a->fd,"sound_wait_for_leader: %s\n", conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, b_profile.sounds));
1850  ast_cli(a->fd,"sound_leader_has_left: %s\n", conf_get_sound(CONF_SOUND_LEADER_HAS_LEFT, b_profile.sounds));
1851  ast_cli(a->fd,"sound_get_pin: %s\n", conf_get_sound(CONF_SOUND_GET_PIN, b_profile.sounds));
1852  ast_cli(a->fd,"sound_invalid_pin: %s\n", conf_get_sound(CONF_SOUND_INVALID_PIN, b_profile.sounds));
1853  ast_cli(a->fd,"sound_locked: %s\n", conf_get_sound(CONF_SOUND_LOCKED, b_profile.sounds));
1854  ast_cli(a->fd,"sound_unlocked_now: %s\n", conf_get_sound(CONF_SOUND_UNLOCKED_NOW, b_profile.sounds));
1855  ast_cli(a->fd,"sound_lockednow: %s\n", conf_get_sound(CONF_SOUND_LOCKED_NOW, b_profile.sounds));
1856  ast_cli(a->fd,"sound_error_menu: %s\n", conf_get_sound(CONF_SOUND_ERROR_MENU, b_profile.sounds));
1857  ast_cli(a->fd,"sound_join: %s\n", conf_get_sound(CONF_SOUND_JOIN, b_profile.sounds));
1858  ast_cli(a->fd,"sound_leave: %s\n", conf_get_sound(CONF_SOUND_LEAVE, b_profile.sounds));
1859  ast_cli(a->fd,"sound_participants_muted: %s\n", conf_get_sound(CONF_SOUND_PARTICIPANTS_MUTED, b_profile.sounds));
1860  ast_cli(a->fd,"sound_participants_unmuted: %s\n", conf_get_sound(CONF_SOUND_PARTICIPANTS_UNMUTED, b_profile.sounds));
1861  ast_cli(a->fd,"sound_begin: %s\n", conf_get_sound(CONF_SOUND_BEGIN, b_profile.sounds));
1862  ast_cli(a->fd,"\n");
1863 
1864  conf_bridge_profile_destroy(&b_profile);
1865  return CLI_SUCCESS;
1866 }
1867 
1868 static char *complete_menu_name(const char *line, const char *word, int pos, int state)
1869 {
1870  int which = 0;
1871  char *res = NULL;
1872  int wordlen = strlen(word);
1873  struct ao2_iterator i;
1874  struct conf_menu *menu = NULL;
1875  RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
1876 
1877  if (!cfg) {
1878  return NULL;
1879  }
1880 
1881  i = ao2_iterator_init(cfg->menus, 0);
1882  while ((menu = ao2_iterator_next(&i))) {
1883  if (!strncasecmp(menu->name, word, wordlen) && ++which > state) {
1884  res = ast_strdup(menu->name);
1885  ao2_ref(menu, -1);
1886  break;
1887  }
1888  ao2_ref(menu, -1);
1889  }
1891 
1892  return res;
1893 }
1894 
1895 static char *handle_cli_confbridge_show_menus(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1896 {
1897  struct ao2_iterator it;
1898  struct conf_menu *menu;
1899  RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
1900 
1901  switch (cmd) {
1902  case CLI_INIT:
1903  e->command = "confbridge show menus";
1904  e->usage =
1905  "Usage: confbridge show profile menus\n";
1906  return NULL;
1907  case CLI_GENERATE:
1908  return NULL;
1909  }
1910 
1911  if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
1912  return NULL;
1913  }
1914 
1915  ast_cli(a->fd,"--------- Menus -----------\n");
1916  ao2_lock(cfg->menus);
1917  it = ao2_iterator_init(cfg->menus, 0);
1918  while ((menu = ao2_iterator_next(&it))) {
1919  ast_cli(a->fd,"%s\n", menu->name);
1920  ao2_ref(menu, -1);
1921  }
1922  ao2_iterator_destroy(&it);
1923  ao2_unlock(cfg->menus);
1924 
1925  return CLI_SUCCESS;
1926 }
1927 
1928 static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1929 {
1930  RAII_VAR(struct conf_menu *, menu, NULL, ao2_cleanup);
1931  RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
1932  struct conf_menu_entry *menu_entry = NULL;
1933  struct conf_menu_action *menu_action = NULL;
1934 
1935  switch (cmd) {
1936  case CLI_INIT:
1937  e->command = "confbridge show menu";
1938  e->usage =
1939  "Usage: confbridge show menu [<menu name>]\n";
1940  return NULL;
1941  case CLI_GENERATE:
1942  if (a->pos == 3) {
1943  return complete_menu_name(a->line, a->word, a->pos, a->n);
1944  }
1945  return NULL;
1946  }
1947 
1948  if (a->argc != 4) {
1949  return CLI_SHOWUSAGE;
1950  }
1951 
1952  if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
1953  return NULL;
1954  }
1955 
1956  if (!(menu = menu_find(cfg->menus, a->argv[3]))) {
1957  ast_cli(a->fd, "No conference menu named '%s' found!\n", a->argv[3]);
1958  return CLI_SUCCESS;
1959  }
1960  ao2_lock(menu);
1961 
1962  ast_cli(a->fd,"Name: %s\n", menu->name);
1963  AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
1964  int action_num = 0;
1965  ast_cli(a->fd, "%s=", menu_entry->dtmf);
1966  AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
1967  if (action_num) {
1968  ast_cli(a->fd, ", ");
1969  }
1970  switch (menu_action->id) {
1971  case MENU_ACTION_TOGGLE_MUTE:
1972  ast_cli(a->fd, "toggle_mute");
1973  break;
1974  case MENU_ACTION_TOGGLE_BINAURAL:
1975  ast_cli(a->fd, "toggle_binaural");
1976  break;
1977  case MENU_ACTION_NOOP:
1978  ast_cli(a->fd, "no_op");
1979  break;
1980  case MENU_ACTION_INCREASE_LISTENING:
1981  ast_cli(a->fd, "increase_listening_volume");
1982  break;
1983  case MENU_ACTION_DECREASE_LISTENING:
1984  ast_cli(a->fd, "decrease_listening_volume");
1985  break;
1986  case MENU_ACTION_RESET_LISTENING:
1987  ast_cli(a->fd, "reset_listening_volume");
1988  break;
1989  case MENU_ACTION_RESET_TALKING:
1990  ast_cli(a->fd, "reset_talking_volume");
1991  break;
1992  case MENU_ACTION_INCREASE_TALKING:
1993  ast_cli(a->fd, "increase_talking_volume");
1994  break;
1995  case MENU_ACTION_DECREASE_TALKING:
1996  ast_cli(a->fd, "decrease_talking_volume");
1997  break;
1998  case MENU_ACTION_PLAYBACK:
1999  ast_cli(a->fd, "playback(%s)", menu_action->data.playback_file);
2000  break;
2001  case MENU_ACTION_PLAYBACK_AND_CONTINUE:
2002  ast_cli(a->fd, "playback_and_continue(%s)", menu_action->data.playback_file);
2003  break;
2004  case MENU_ACTION_DIALPLAN_EXEC:
2005  ast_cli(a->fd, "dialplan_exec(%s,%s,%d)",
2006  menu_action->data.dialplan_args.context,
2007  menu_action->data.dialplan_args.exten,
2008  menu_action->data.dialplan_args.priority);
2009  break;
2010  case MENU_ACTION_ADMIN_TOGGLE_LOCK:
2011  ast_cli(a->fd, "admin_toggle_conference_lock");
2012  break;
2013  case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
2014  ast_cli(a->fd, "admin_toggle_mute_participants");
2015  break;
2016  case MENU_ACTION_PARTICIPANT_COUNT:
2017  ast_cli(a->fd, "participant_count");
2018  break;
2019  case MENU_ACTION_ADMIN_KICK_LAST:
2020  ast_cli(a->fd, "admin_kick_last");
2021  break;
2022  case MENU_ACTION_LEAVE:
2023  ast_cli(a->fd, "leave_conference");
2024  break;
2025  case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
2026  ast_cli(a->fd, "set_as_single_video_src");
2027  break;
2028  case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
2029  ast_cli(a->fd, "release_as_single_video_src");
2030  break;
2031  }
2032  action_num++;
2033  }
2034  ast_cli(a->fd,"\n");
2035  }
2036 
2037 
2038  ao2_unlock(menu);
2039  return CLI_SUCCESS;
2040 }
2041 
2042 static struct ast_cli_entry cli_confbridge_parser[] = {
2043  AST_CLI_DEFINE(handle_cli_confbridge_show_user_profile, "Show a conference user profile."),
2044  AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profile, "Show a conference bridge profile."),
2045  AST_CLI_DEFINE(handle_cli_confbridge_show_menu, "Show a conference menu"),
2046  AST_CLI_DEFINE(handle_cli_confbridge_show_user_profiles, "Show a list of conference user profiles."),
2047  AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profiles, "Show a list of conference bridge profiles."),
2048  AST_CLI_DEFINE(handle_cli_confbridge_show_menus, "Show a list of conference menus"),
2049 
2050 };
2051 
2052 static void confbridge_cfg_destructor(void *obj)
2053 {
2054  struct confbridge_cfg *cfg = obj;
2055  ao2_cleanup(cfg->user_profiles);
2056  ao2_cleanup(cfg->bridge_profiles);
2057  ao2_cleanup(cfg->menus);
2058 }
2059 
2060 void *confbridge_cfg_alloc(void)
2061 {
2062  struct confbridge_cfg *cfg;
2063 
2064  if (!(cfg = ao2_alloc(sizeof(*cfg), confbridge_cfg_destructor))) {
2065  return NULL;
2066  }
2067 
2068  cfg->user_profiles = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, 283,
2069  user_hash_cb, NULL, user_cmp_cb);
2070  if (!cfg->user_profiles) {
2071  goto error;
2072  }
2073 
2074  cfg->bridge_profiles = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, 283,
2075  bridge_hash_cb, NULL, bridge_cmp_cb);
2076  if (!cfg->bridge_profiles) {
2077  goto error;
2078  }
2079 
2081  menu_hash_cb, NULL, menu_cmp_cb);
2082  if (!cfg->menus) {
2083  goto error;
2084  }
2085 
2086  return cfg;
2087 error:
2088  ao2_ref(cfg, -1);
2089  return NULL;
2090 }
2091 
2092 static int announce_user_count_all_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
2093 {
2094  struct user_profile *u_profile = obj;
2095 
2096  if (strcasecmp(var->name, "announce_user_count_all")) {
2097  return -1;
2098  }
2099  if (ast_true(var->value)) {
2100  u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL;
2101  } else if (ast_false(var->value)) {
2102  u_profile->flags = u_profile->flags & ~USER_OPT_ANNOUNCEUSERCOUNTALL;
2103  } else if (sscanf(var->value, "%30u", &u_profile->announce_user_count_all_after) == 1) {
2104  u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL;
2105  } else {
2106  return -1;
2107  }
2108  return 0;
2109 }
2110 
2111 static int mix_interval_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
2112 {
2113  struct bridge_profile *b_profile = obj;
2114 
2115  if (strcasecmp(var->name, "mixing_interval")) {
2116  return -1;
2117  }
2118  if (sscanf(var->value, "%30u", &b_profile->mix_interval) != 1) {
2119  return -1;
2120  }
2121  switch (b_profile->mix_interval) {
2122  case 10:
2123  case 20:
2124  case 40:
2125  case 80:
2126  return 0;
2127  default:
2128  return -1;
2129  }
2130 }
2131 
2132 static int video_mode_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
2133 {
2134  struct bridge_profile *b_profile = obj;
2135 
2136  if (strcasecmp(var->name, "video_mode")) {
2137  return -1;
2138  }
2139  if (!strcasecmp(var->value, "first_marked")) {
2140  ast_set_flags_to(b_profile,
2141  BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
2142  | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
2143  | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER
2144  | BRIDGE_OPT_VIDEO_SRC_SFU,
2145  BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED);
2146  } else if (!strcasecmp(var->value, "last_marked")) {
2147  ast_set_flags_to(b_profile,
2148  BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
2149  | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
2150  | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER
2151  | BRIDGE_OPT_VIDEO_SRC_SFU,
2152  BRIDGE_OPT_VIDEO_SRC_LAST_MARKED);
2153  } else if (!strcasecmp(var->value, "follow_talker")) {
2154  ast_set_flags_to(b_profile,
2155  BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
2156  | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
2157  | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER
2158  | BRIDGE_OPT_VIDEO_SRC_SFU,
2159  BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
2160  } else if (!strcasecmp(var->value, "sfu")) {
2161  ast_set_flags_to(b_profile,
2162  BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
2163  | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
2164  | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER
2165  | BRIDGE_OPT_VIDEO_SRC_SFU,
2166  BRIDGE_OPT_VIDEO_SRC_SFU);
2167  } else if (!strcasecmp(var->value, "none")) {
2168  ast_clear_flag(b_profile,
2169  BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
2170  | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
2171  | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER
2172  | BRIDGE_OPT_VIDEO_SRC_SFU);
2173  } else {
2174  return -1;
2175  }
2176  return 0;
2177 }
2178 
2179 static int remb_behavior_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
2180 {
2181  struct bridge_profile *b_profile = obj;
2182 
2183  if (strcasecmp(var->name, "remb_behavior")) {
2184  return -1;
2185  }
2186 
2187  ast_clear_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE |
2188  BRIDGE_OPT_REMB_BEHAVIOR_LOWEST |
2189  BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST |
2190  BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE_ALL |
2191  BRIDGE_OPT_REMB_BEHAVIOR_LOWEST_ALL |
2192  BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST_ALL |
2193  BRIDGE_OPT_REMB_BEHAVIOR_FORCE
2194  );
2195 
2196  if (!strcasecmp(var->value, "average")) {
2197  ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE);
2198  } else if (!strcasecmp(var->value, "lowest")) {
2199  ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_LOWEST);
2200  } else if (!strcasecmp(var->value, "highest")) {
2201  ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST);
2202  } else if (!strcasecmp(var->value, "average_all")) {
2203  ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE_ALL);
2204  } else if (!strcasecmp(var->value, "lowest_all")) {
2205  ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_LOWEST_ALL);
2206  } else if (!strcasecmp(var->value, "highest_all")) {
2207  ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST_ALL);
2208  } else if (!strcasecmp(var->value, "force")) {
2209  ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_FORCE);
2210  } else {
2211  return -1;
2212  }
2213  return 0;
2214 }
2215 
2216 static int user_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
2217 {
2218  struct user_profile *u_profile = obj;
2219 
2220  return conf_find_user_profile(NULL, var->value, u_profile) ? 0 : -1;
2221 }
2222 
2223 static int sample_rate_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
2224 {
2225  struct bridge_profile *b_profile = obj;
2226  unsigned int *slot;
2227 
2228  if (!strcasecmp(var->name, "internal_sample_rate")) {
2229  slot = &b_profile->internal_sample_rate;
2230  if (!strcasecmp(var->value, "auto")) {
2231  *slot = 0;
2232  return 0;
2233  }
2234  } else if (!strcasecmp(var->name, "maximum_sample_rate")) {
2235  slot = &b_profile->maximum_sample_rate;
2236  if (!strcasecmp(var->value, "none")) {
2237  *slot = 0;
2238  return 0;
2239  }
2240  } else {
2241  return -1;
2242  }
2243 
2244  return ast_parse_arg(var->value, PARSE_UINT32 | PARSE_IN_RANGE, slot, 8000, 192000);
2245 }
2246 
2247 static int bridge_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
2248 {
2249  struct bridge_profile *b_profile = obj;
2250  struct bridge_profile_sounds *sounds = bridge_profile_sounds_alloc();
2251  struct bridge_profile_sounds *oldsounds = b_profile->sounds;
2252 
2253  if (!sounds) {
2254  return -1;
2255  }
2256  if (!(conf_find_bridge_profile(NULL, var->value, b_profile))) {
2257  ao2_ref(sounds, -1);
2258  return -1;
2259  }
2260  /* Using a bridge profile as a template is a little complicated due to the sounds. Since the sounds
2261  * structure of a dynamic profile will need to be altered, a completely new sounds structure must be
2262  * created instead of simply holding a reference to the one built by the config file. */
2263  ast_string_field_set(sounds, onlyperson, b_profile->sounds->onlyperson);
2264  ast_string_field_set(sounds, onlyone, b_profile->sounds->onlyone);
2265  ast_string_field_set(sounds, hasjoin, b_profile->sounds->hasjoin);
2266  ast_string_field_set(sounds, hasleft, b_profile->sounds->hasleft);
2267  ast_string_field_set(sounds, kicked, b_profile->sounds->kicked);
2268  ast_string_field_set(sounds, muted, b_profile->sounds->muted);
2269  ast_string_field_set(sounds, unmuted, b_profile->sounds->unmuted);
2270  ast_string_field_set(sounds, thereare, b_profile->sounds->thereare);
2271  ast_string_field_set(sounds, otherinparty, b_profile->sounds->otherinparty);
2272  ast_string_field_set(sounds, placeintoconf, b_profile->sounds->placeintoconf);
2273  ast_string_field_set(sounds, waitforleader, b_profile->sounds->waitforleader);
2274  ast_string_field_set(sounds, leaderhasleft, b_profile->sounds->leaderhasleft);
2275  ast_string_field_set(sounds, getpin, b_profile->sounds->getpin);
2276  ast_string_field_set(sounds, invalidpin, b_profile->sounds->invalidpin);
2277  ast_string_field_set(sounds, locked, b_profile->sounds->locked);
2278  ast_string_field_set(sounds, unlockednow, b_profile->sounds->unlockednow);
2279  ast_string_field_set(sounds, lockednow, b_profile->sounds->lockednow);
2280  ast_string_field_set(sounds, errormenu, b_profile->sounds->errormenu);
2281  ast_string_field_set(sounds, join, b_profile->sounds->join);
2282  ast_string_field_set(sounds, leave, b_profile->sounds->leave);
2283  ast_string_field_set(sounds, participantsmuted, b_profile->sounds->participantsmuted);
2284  ast_string_field_set(sounds, participantsunmuted, b_profile->sounds->participantsunmuted);
2285  ast_string_field_set(sounds, begin, b_profile->sounds->begin);
2286 
2287  ao2_ref(b_profile->sounds, -1); /* sounds struct copied over to it from the template by reference only. */
2288  ao2_ref(oldsounds, -1); /* original sounds struct we don't need anymore */
2289  b_profile->sounds = sounds; /* the new sounds struct that is a deep copy of the one from the template. */
2290 
2291  return 0;
2292 }
2293 
2294 static int copy_menu_entry(struct conf_menu_entry *dst, struct conf_menu_entry *src)
2295 {
2296  struct conf_menu_action *menu_action;
2297  struct conf_menu_action *new_menu_action;
2298 
2299  ast_copy_string(dst->dtmf, src->dtmf, sizeof(dst->dtmf));
2301 
2302  AST_LIST_TRAVERSE(&src->actions, menu_action, action) {
2303  if (!(new_menu_action = ast_calloc(1, sizeof(*new_menu_action)))) {
2304  return -1;
2305  }
2306  memcpy(new_menu_action, menu_action, sizeof(*new_menu_action));
2307  AST_LIST_NEXT(new_menu_action, action) = NULL;
2308  AST_LIST_INSERT_TAIL(&dst->actions, new_menu_action, action);
2309  }
2310 
2311  return 0;
2312 }
2313 
2314 static int conf_menu_profile_copy(struct conf_menu *dst, struct conf_menu *src)
2315 {
2316  /* Copy each menu item to the dst struct */
2317  struct conf_menu_entry *cur;
2318 
2319  AST_LIST_TRAVERSE(&src->entries, cur, entry) {
2320  struct conf_menu_entry *cpy;
2321 
2322  if (!(cpy = ast_calloc(1, sizeof(*cpy)))) {
2323  return -1;
2324  }
2325 
2326  if (copy_menu_entry(cpy, cur)) {
2328  ast_free(cpy);
2329  return -1;
2330  }
2331  AST_LIST_INSERT_TAIL(&dst->entries, cpy, entry);
2332  }
2333 
2334  return 0;
2335 }
2336 
2337 static int menu_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
2338 {
2339  struct conf_menu *dst_menu = obj;
2340  RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
2341  RAII_VAR(struct conf_menu *, src_menu, NULL, ao2_cleanup);
2342 
2343  if (!cfg) {
2344  return 0;
2345  }
2346 
2347  if (!(src_menu = ao2_find(cfg->menus, var->value, OBJ_KEY))) {
2348  return -1;
2349  }
2350 
2351  if (conf_menu_profile_copy(dst_menu, src_menu)) {
2352  return -1;
2353  }
2354 
2355  return 0;
2356 }
2357 
2358 static int sound_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
2359 {
2360  set_sound(var->name, var->value, obj);
2361  return 0;
2362 }
2363 
2364 static int menu_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
2365 {
2366  add_menu_entry(obj, var->name, var->value);
2367  return 0;
2368 }
2369 
2370 static int verify_default_profiles(void)
2371 {
2372  RAII_VAR(struct user_profile *, user_profile, NULL, ao2_cleanup);
2373  RAII_VAR(struct bridge_profile *, bridge_profile, NULL, ao2_cleanup);
2374  RAII_VAR(struct conf_menu *, menu_profile, NULL, ao2_cleanup);
2375  /* We can only be called as a result of an aco_process_config so this is safe */
2376  struct confbridge_cfg *cfg = aco_pending_config(&cfg_info);
2377 
2378  if (!cfg) {
2379  return 0;
2380  }
2381 
2382  bridge_profile = ao2_find(cfg->bridge_profiles, DEFAULT_BRIDGE_PROFILE, OBJ_KEY);
2383  if (!bridge_profile) {
2384  bridge_profile = bridge_profile_alloc(DEFAULT_BRIDGE_PROFILE);
2385  if (!bridge_profile) {
2386  return -1;
2387  }
2388  ast_log(AST_LOG_NOTICE, "Adding %s profile to app_confbridge\n", DEFAULT_BRIDGE_PROFILE);
2389  aco_set_defaults(&bridge_type, DEFAULT_BRIDGE_PROFILE, bridge_profile);
2390  ao2_link(cfg->bridge_profiles, bridge_profile);
2391  }
2392 
2393  user_profile = ao2_find(cfg->user_profiles, DEFAULT_USER_PROFILE, OBJ_KEY);
2394  if (!user_profile) {
2395  user_profile = user_profile_alloc(DEFAULT_USER_PROFILE);
2396  if (!user_profile) {
2397  return -1;
2398  }
2399  ast_log(AST_LOG_NOTICE, "Adding %s profile to app_confbridge\n", DEFAULT_USER_PROFILE);
2400  aco_set_defaults(&user_type, DEFAULT_USER_PROFILE, user_profile);
2401  ao2_link(cfg->user_profiles, user_profile);
2402  }
2403 
2404  menu_profile = ao2_find(cfg->menus, DEFAULT_MENU_PROFILE, OBJ_KEY);
2405  if (!menu_profile) {
2406  menu_profile = menu_alloc(DEFAULT_MENU_PROFILE);
2407  if (!menu_profile) {
2408  return -1;
2409  }
2410  ast_log(AST_LOG_NOTICE, "Adding %s menu to app_confbridge\n", DEFAULT_MENU_PROFILE);
2411  aco_set_defaults(&menu_type, DEFAULT_MENU_PROFILE, menu_profile);
2412  ao2_link(cfg->menus, menu_profile);
2413  }
2414 
2415  return 0;
2416 }
2417 
2419 {
2420  if (aco_info_init(&cfg_info)) {
2421  return -1;
2422  }
2423 
2424  /* User options */
2425  aco_option_register(&cfg_info, "type", ACO_EXACT, user_types, NULL, OPT_NOOP_T, 0, 0);
2426  aco_option_register(&cfg_info, "admin", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ADMIN);
2427  aco_option_register(&cfg_info, "send_events", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_SEND_EVENTS);
2428  aco_option_register(&cfg_info, "echo_events", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ECHO_EVENTS);
2429  aco_option_register(&cfg_info, "marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_MARKEDUSER);
2430  aco_option_register(&cfg_info, "startmuted", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_STARTMUTED);
2431  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);
2432  aco_option_register(&cfg_info, "quiet", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_QUIET);
2433  aco_option_register(&cfg_info, "hear_own_join_sound", ACO_EXACT, user_types, "yes", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_HEAR_OWN_JOIN_SOUND);
2434  aco_option_register_custom(&cfg_info, "announce_user_count_all", ACO_EXACT, user_types, "no", announce_user_count_all_handler, 0);
2435  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);
2436  /* Negative logic. Defaults to "yes" and evaluates with ast_false(). If !ast_false(), USER_OPT_NOONLYPERSON is cleared */
2437  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);
2438  aco_option_register(&cfg_info, "wait_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_WAITMARKED);
2439  aco_option_register(&cfg_info, "end_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKED);
2440  aco_option_register(&cfg_info, "end_marked_any", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKEDANY);
2441  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);
2442  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);
2443  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);
2444  aco_option_register(&cfg_info, "announce_join_leave_review", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW);
2445  aco_option_register(&cfg_info, "pin", ACO_EXACT, user_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct user_profile, pin));
2446  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));
2447  aco_option_register(&cfg_info, "announcement", ACO_EXACT, user_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct user_profile, announcement));
2448  aco_option_register(&cfg_info, "denoise", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_DENOISE);
2449  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);
2450  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));
2451  aco_option_register(&cfg_info, "dsp_talking_threshold", ACO_EXACT, user_types, __stringify(DEFAULT_TALKING_THRESHOLD), OPT_UINT_T, 0, FLDSET(struct user_profile, talking_threshold));
2452  aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_JITTERBUFFER);
2453  aco_option_register(&cfg_info, "timeout", ACO_EXACT, user_types, "0", OPT_UINT_T, 0, FLDSET(struct user_profile, timeout));
2454  aco_option_register(&cfg_info, "text_messaging", ACO_EXACT, user_types, "yes", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_TEXT_MESSAGING);
2455  aco_option_register(&cfg_info, "answer_channel", ACO_EXACT, user_types, "yes", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ANSWER_CHANNEL);
2456 
2457  /* This option should only be used with the CONFBRIDGE dialplan function */
2458  aco_option_register_custom(&cfg_info, "template", ACO_EXACT, user_types, NULL, user_template_handler, 0);
2459 
2460 /* XXX ASTERISK-21271 need a user supplied bridge merge_priority to merge ConfBridges (default = 1, range 1-INT_MAX) */
2461  /* Bridge options */
2462  aco_option_register(&cfg_info, "type", ACO_EXACT, bridge_types, NULL, OPT_NOOP_T, 0, 0);
2463  aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), USER_OPT_JITTERBUFFER);
2464  aco_option_register_custom(&cfg_info, "internal_sample_rate", ACO_EXACT, bridge_types, "auto", sample_rate_handler, 0);
2465  aco_option_register(&cfg_info, "binaural_active", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), BRIDGE_OPT_BINAURAL_ACTIVE);
2466  aco_option_register_custom(&cfg_info, "maximum_sample_rate", ACO_EXACT, bridge_types, "none", sample_rate_handler, 0);
2467  aco_option_register_custom(&cfg_info, "mixing_interval", ACO_EXACT, bridge_types, "20", mix_interval_handler, 0);
2468  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);
2469  aco_option_register_custom(&cfg_info, "video_mode", ACO_EXACT, bridge_types, NULL, video_mode_handler, 0);
2470  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);
2471  aco_option_register(&cfg_info, "record_file_timestamp", ACO_EXACT, bridge_types, "yes", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), BRIDGE_OPT_RECORD_FILE_TIMESTAMP);
2472  aco_option_register(&cfg_info, "max_members", ACO_EXACT, bridge_types, "0", OPT_UINT_T, 0, FLDSET(struct bridge_profile, max_members));
2473  aco_option_register(&cfg_info, "record_file", ACO_EXACT, bridge_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, rec_file));
2474  aco_option_register(&cfg_info, "record_options", ACO_EXACT, bridge_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, rec_options));
2475  aco_option_register(&cfg_info, "record_command", ACO_EXACT, bridge_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, rec_command));
2476  aco_option_register(&cfg_info, "regcontext", ACO_EXACT, bridge_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, regcontext));
2477  aco_option_register(&cfg_info, "language", ACO_EXACT, bridge_types, "en", OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, language));
2478  aco_option_register_custom(&cfg_info, "sound_", ACO_PREFIX, bridge_types, NULL, sound_option_handler, 0);
2479  aco_option_register(&cfg_info, "video_update_discard", ACO_EXACT, bridge_types, "2000", OPT_UINT_T, 0, FLDSET(struct bridge_profile, video_update_discard));
2480  aco_option_register(&cfg_info, "remb_send_interval", ACO_EXACT, bridge_types, "0", OPT_UINT_T, 0, FLDSET(struct bridge_profile, remb_send_interval));
2481  aco_option_register_custom(&cfg_info, "remb_behavior", ACO_EXACT, bridge_types, "average", remb_behavior_handler, 0);
2482  aco_option_register(&cfg_info, "remb_estimated_bitrate", ACO_EXACT, bridge_types, "0", OPT_UINT_T, 0, FLDSET(struct bridge_profile, remb_estimated_bitrate));
2483  aco_option_register(&cfg_info, "enable_events", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), BRIDGE_OPT_ENABLE_EVENTS);
2484  /* This option should only be used with the CONFBRIDGE dialplan function */
2485  aco_option_register_custom(&cfg_info, "template", ACO_EXACT, bridge_types, NULL, bridge_template_handler, 0);
2486 
2487  /* Menu options */
2488  aco_option_register(&cfg_info, "type", ACO_EXACT, menu_types, NULL, OPT_NOOP_T, 0, 0);
2489  /* This option should only be used with the CONFBRIDGE dialplan function */
2490  aco_option_register_custom(&cfg_info, "template", ACO_EXACT, menu_types, NULL, menu_template_handler, 0);
2491  aco_option_register_custom(&cfg_info, "^[0-9A-D*#]+$", ACO_REGEX, menu_types, NULL, menu_option_handler, 0);
2492 
2493  if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
2494  goto error;
2495  }
2496 
2497  if (ast_cli_register_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser))) {
2498  goto error;
2499  }
2500 
2501  return 0;
2502 error:
2504  return -1;
2505 }
2506 
2508 {
2509  if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
2510  /* On a reload, just keep the config we already have in place. */
2511  return -1;
2512  }
2513  return 0;
2514 }
2515 
2516 static void conf_user_profile_copy(struct user_profile *dst, struct user_profile *src)
2517 {
2518  *dst = *src;
2519 }
2520 
2521 const struct user_profile *conf_find_user_profile(struct ast_channel *chan, const char *user_profile_name, struct user_profile *result)
2522 {
2523  struct user_profile *tmp2;
2524  struct ast_datastore *datastore = NULL;
2525  struct func_confbridge_data *b_data = NULL;
2526  RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
2527 
2528  if (chan && ast_strlen_zero(user_profile_name)) {
2529  ast_channel_lock(chan);
2530  datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
2531  ast_channel_unlock(chan);
2532  if (datastore) {
2533  b_data = datastore->data;
2534  if (b_data->u_usable) {
2535  conf_user_profile_copy(result, &b_data->u_profile);
2536  return result;
2537  }
2538  }
2539  }
2540 
2541  if (!cfg) {
2542  return NULL;
2543  }
2544  if (ast_strlen_zero(user_profile_name)) {
2545  user_profile_name = DEFAULT_USER_PROFILE;
2546  }
2547  if (!(tmp2 = ao2_find(cfg->user_profiles, user_profile_name, OBJ_KEY))) {
2548  return NULL;
2549  }
2550  ao2_lock(tmp2);
2551  conf_user_profile_copy(result, tmp2);
2552  ao2_unlock(tmp2);
2553  ao2_ref(tmp2, -1);
2554 
2555  return result;
2556 }
2557 
2559 {
2560  *dst = *src;
2561  if (src->sounds) {
2562  ao2_ref(src->sounds, +1);
2563  }
2564 }
2565 
2567 {
2568  if (b_profile->sounds) {
2569  ao2_ref(b_profile->sounds, -1);
2570  b_profile->sounds = NULL;
2571  }
2572 }
2573 
2574 const struct bridge_profile *conf_find_bridge_profile(struct ast_channel *chan, const char *bridge_profile_name, struct bridge_profile *result)
2575 {
2576  struct bridge_profile *tmp2;
2577  struct ast_datastore *datastore = NULL;
2578  struct func_confbridge_data *b_data = NULL;
2579  RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
2580 
2581  if (chan && ast_strlen_zero(bridge_profile_name)) {
2582  ast_channel_lock(chan);
2583  datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
2584  ast_channel_unlock(chan);
2585  if (datastore) {
2586  b_data = datastore->data;
2587  if (b_data->b_usable) {
2588  conf_bridge_profile_copy(result, &b_data->b_profile);
2589  return result;
2590  }
2591  }
2592  }
2593 
2594  if (!cfg) {
2595  return NULL;
2596  }
2597  if (ast_strlen_zero(bridge_profile_name)) {
2598  bridge_profile_name = DEFAULT_BRIDGE_PROFILE;
2599  }
2600  if (!(tmp2 = ao2_find(cfg->bridge_profiles, bridge_profile_name, OBJ_KEY))) {
2601  return NULL;
2602  }
2603  ao2_lock(tmp2);
2604  conf_bridge_profile_copy(result, tmp2);
2605  ao2_unlock(tmp2);
2606  ao2_ref(tmp2, -1);
2607 
2608  return result;
2609 }
2610 
2612  struct confbridge_user *user;
2613  struct conf_menu_entry menu_entry;
2614  struct conf_menu *menu;
2615 };
2616 
2617 static void menu_hook_destroy(void *hook_pvt)
2618 {
2619  struct dtmf_menu_hook_pvt *pvt = hook_pvt;
2620  struct conf_menu_action *action = NULL;
2621 
2622  ao2_cleanup(pvt->menu);
2623 
2624  while ((action = AST_LIST_REMOVE_HEAD(&pvt->menu_entry.actions, action))) {
2625  ast_free(action);
2626  }
2627  ast_free(pvt);
2628 }
2629 
2630 static int menu_hook_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
2631 {
2632  struct dtmf_menu_hook_pvt *pvt = hook_pvt;
2633 
2634  return conf_handle_dtmf(bridge_channel, pvt->user, &pvt->menu_entry, pvt->menu);
2635 }
2636 
2638 {
2639  struct conf_menu_action *menu_action = NULL;
2640  while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
2641  ast_free(menu_action);
2642  }
2643 }
2644 
2645 int conf_find_menu_entry_by_sequence(const char *dtmf_sequence, struct conf_menu *menu, struct conf_menu_entry *result)
2646 {
2647  struct conf_menu_entry *menu_entry = NULL;
2648 
2649  ao2_lock(menu);
2650  AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
2651  if (!strcasecmp(menu_entry->dtmf, dtmf_sequence)) {
2652  copy_menu_entry(result, menu_entry);
2653  ao2_unlock(menu);
2654  return 1;
2655  }
2656  }
2657  ao2_unlock(menu);
2658 
2659  return 0;
2660 }
2661 
2662 static int apply_menu_to_user(struct confbridge_user *user, struct conf_menu *menu)
2663 {
2664  struct conf_menu_entry *menu_entry;
2665 
2666  SCOPED_AO2LOCK(menu_lock, menu);
2667  AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
2668  struct dtmf_menu_hook_pvt *pvt;
2669 
2670  if (!(pvt = ast_calloc(1, sizeof(*pvt)))) {
2671  return -1;
2672  }
2673  pvt->user = user;
2674  pvt->menu = ao2_bump(menu);
2675 
2676  if (copy_menu_entry(&pvt->menu_entry, menu_entry)) {
2677  menu_hook_destroy(pvt);
2678  return -1;
2679  }
2680 
2681  if (ast_bridge_dtmf_hook(&user->features, pvt->menu_entry.dtmf,
2682  menu_hook_callback, pvt, menu_hook_destroy, 0)) {
2683  menu_hook_destroy(pvt);
2684  }
2685  }
2686  strcpy(user->menu_name, menu->name); /* Safe */
2687 
2688  return 0;
2689 }
2690 
2691 int conf_set_menu_to_user(struct ast_channel *chan, struct confbridge_user *user, const char *menu_profile_name)
2692 {
2693  RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
2694  RAII_VAR(struct conf_menu *, menu, NULL, ao2_cleanup);
2695 
2696  if (chan && ast_strlen_zero(menu_profile_name)) {
2697  struct ast_datastore *datastore;
2698  struct func_confbridge_data *b_data;
2699 
2700  ast_channel_lock(chan);
2701  datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
2702  ast_channel_unlock(chan);
2703  if (datastore) {
2704  /* If a menu exists in the CONFBRIDGE function datastore, use it. */
2705  b_data = datastore->data;
2706  if (b_data->m_usable) {
2707  menu = ao2_bump(b_data->menu);
2708  return apply_menu_to_user(user, menu);
2709  }
2710  }
2711  }
2712 
2713  /* Otherwise, we need to get whatever menu profile is specified to use (or default). */
2714  if (!cfg) {
2715  return -1;
2716  }
2717 
2718  if (ast_strlen_zero(menu_profile_name)) {
2719  menu_profile_name = DEFAULT_MENU_PROFILE;
2720  }
2721 
2722  if (!(menu = ao2_find(cfg->menus, menu_profile_name, OBJ_KEY))) {
2723  return -1;
2724  }
2725 
2726  return apply_menu_to_user(user, menu);
2727 }
2728 
2730 {
2731  ast_cli_unregister_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser));
2732  aco_info_destroy(&cfg_info);
2733  ao2_global_obj_release(cfg_handle);
2734 }
const char * type
Definition: datastore.h:32
Main Channel structure associated with a channel.
static int user_cmp_cb(void *obj, void *arg, int flags)
Asterisk main include file. File version handling, generic pbx functions.
int ast_parse_arg(const char *arg, enum ast_parse_flags flags, void *p_result,...)
The argument parsing routine.
Definition: main/config.c:3827
#define aco_option_register_custom(info, name, matchtype, types, default_val, handler, flags)
Register a config option.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
const char * conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds *custom_sounds)
Looks to see if sound file is stored in bridge profile sounds, if not default sound is provided...
Definition: confbridge.h:138
#define OBJ_KEY
Definition: astobj2.h:1151
#define OBJ_POINTER
Definition: astobj2.h:1150
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
descriptor for a cli entry.
Definition: cli.h:171
static const int DEFAULT_SILENCE_THRESHOLD
The default silence threshold we will use if an alternate configured value is not present or is inval...
Definition: dsp.c:245
const struct user_profile * conf_find_user_profile(struct ast_channel *chan, const char *user_profile_name, struct user_profile *result)
find a user profile given a user profile's name and store that profile in result structure.
#define aco_option_register(info, name, matchtype, types, default_val, opt_type, flags,...)
Register a config option.
Structure for variables, used for configurations and for channel variables.
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:439
void conf_menu_entry_destroy(struct conf_menu_entry *menu_entry)
Destroys and frees all the actions stored in a menu_entry structure.
#define CHARFLDSET(type, field)
A helper macro to pass the appropriate arguments to aco_option_register for OPT_CHAR_ARRAY_T.
Type for a default handler that should do nothing.
Structure for a data store type.
Definition: datastore.h:31
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
enum aco_process_status aco_process_config(struct aco_info *info, int reload)
Process a config info via the options registered with an aco_info.
#define ao2_global_obj_ref(holder)
Get a reference to the object stored in the global holder.
Definition: astobj2.h:918
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
unsigned int silence_threshold
Definition: confbridge.h:164
Type for default option handler for bools (ast_true/ast_false) that are stored in a flag...
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
Structure for a data store object.
Definition: datastore.h:64
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2399
The representation of a single configuration file to be processed.
enum aco_type_t type
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define ACO_TYPES(...)
A helper macro to ensure that aco_info types always have a sentinel.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
char dtmf[MAXIMUM_DTMF_FEATURE_STRING]
Definition: confbridge.h:140
#define OBJ_PARTIAL_KEY
Definition: astobj2.h:1152
void conf_bridge_profile_copy(struct bridge_profile *dst, struct bridge_profile *src)
copies a bridge profile
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
Type for default option handler for character array strings.
Configuration File Parser.
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
int aco_info_init(struct aco_info *info)
Initialize an aco_info structure.
static void bridge_profile_sounds_destroy_cb(void *obj)
void * aco_pending_config(struct aco_info *info)
Get pending config changes.
Type for default option handler for unsigned integers.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel, struct confbridge_user *user, struct conf_menu_entry *menu_entry, struct conf_menu *menu)
Once a DTMF sequence matches a sequence in the user's DTMF menu, this function will get called to per...
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
void conf_bridge_profile_destroy(struct bridge_profile *b_profile)
Destroy a bridge profile found by 'conf_find_bridge_profile'.
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
int conf_reload_config(void)
reload confbridge.conf file
struct ao2_container * container
Definition: res_fax.c:501
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
char language[MAX_LANGUAGE]
Definition: confbridge.h:229
unsigned int maximum_sample_rate
Definition: confbridge.h:236
Core PBX routines and definitions.
unsigned int internal_sample_rate
Definition: confbridge.h:235
static struct stasis_rest_handlers sounds
REST handler for /api-docs/sounds.json.
Their was an error and no changes were applied.
static int menu_cmp_cb(void *obj, void *arg, int flags)
Configuration option-handling.
unsigned int video_update_discard
Definition: confbridge.h:240
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: utils.c:2199
void conf_destroy_config(void)
destroy the information loaded from the confbridge.conf file
void aco_info_destroy(struct aco_info *info)
Destroy an initialized aco_info struct.
#define ao2_global_obj_release(holder)
Release the ao2 object held in the global holder.
Definition: astobj2.h:859
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
unsigned int max_members
Definition: confbridge.h:234
unsigned int talking_threshold
Definition: confbridge.h:162
int conf_set_menu_to_user(struct ast_channel *chan, struct confbridge_user *user, const char *menu_profile_name)
find a menu profile given a menu profile's name and apply the menu in DTMF hooks. ...
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition: lock.h:604
char * command
Definition: cli.h:186
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
static int user_hash_cb(const void *obj, const int flags)
Definition: chan_iax2.c:2032
int aco_set_defaults(struct aco_type *type, const char *category, void *obj)
Set all default options of obj.
struct ast_bridge_features features
Definition: confbridge.h:280
Support for logging to various files, console and syslog Configuration in file logger.conf.
int conf_find_menu_entry_by_sequence(const char *dtmf_sequence, struct conf_menu *menu, struct conf_menu_entry *result)
Finds a menu_entry in a menu structure matched by DTMF sequence.
structure to hold users read from users.conf
int ast_bridge_dtmf_hook(struct ast_bridge_features *features, const char *dtmf, ast_bridge_hook_callback callback, void *hook_pvt, ast_bridge_hook_pvt_destructor destructor, enum ast_bridge_hook_remove_flags remove_flags)
Attach a DTMF hook to a bridge features structure.
Definition: bridge.c:3182
const char * usage
Definition: cli.h:177
void * data
Definition: datastore.h:66
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:681
Structure that contains information regarding a channel in a bridge.
char menu_name[MAX_PROFILE_NAME]
Definition: confbridge.h:277
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Standard Command Line Interface.
Type information about a category-level configurable object.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
static int bridge_cmp_cb(void *obj, void *arg, int flags)
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"...
Definition: utils.c:2216
const char * filename
The structure that represents a conference bridge user.
Definition: confbridge.h:273
Definition: search.h:40
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
int conf_load_config(void)
load confbridge.conf file
Generic container type.
int aco_process_var(struct aco_type *type, const char *cat, struct ast_variable *var, void *obj)
Parse a single ast_variable and apply it to an object.
#define AO2_GLOBAL_OBJ_STATIC(name)
Define a global object holder to be used to hold an ao2 object, statically initialized.
Definition: astobj2.h:847
unsigned int remb_send_interval
Definition: confbridge.h:241
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
unsigned int mix_interval
Definition: confbridge.h:237
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
struct conf_menu_entry::@87 actions
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
#define CONFIG_INFO_STANDARD(name, arr, alloc,...)
Declare an aco_info struct with default module and preload values.
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2394
Channel Bridging API.
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
Definition: strings.h:1303
#define AST_APP_ARG(name)
Define an application argument.
const struct bridge_profile * conf_find_bridge_profile(struct ast_channel *chan, const char *bridge_profile_name, struct bridge_profile *result)
Find a bridge profile given a bridge profile's name and store that profile in result structure...
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532