conf_config_parser.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2011, Digium, Inc.
00005  *
00006  * David Vossel <dvossel@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief ConfBridge config parser
00022  *
00023  * \author David Vossel <dvossel@digium.com>
00024  */
00025 
00026 /*** MODULEINFO
00027    <support_level>core</support_level>
00028  ***/
00029 
00030 #include "asterisk.h"
00031 
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 431136 $")
00033 #include "asterisk/logger.h"
00034 #include "asterisk/config.h"
00035 #include "asterisk/config_options.h"
00036 #include "include/confbridge.h"
00037 #include "asterisk/astobj2.h"
00038 #include "asterisk/cli.h"
00039 #include "asterisk/bridge_features.h"
00040 #include "asterisk/stringfields.h"
00041 #include "asterisk/pbx.h"
00042 
00043 
00044 /*** DOCUMENTATION
00045    <configInfo name="app_confbridge" language="en_US">
00046       <synopsis>Conference Bridge Application</synopsis>
00047       <configFile name="confbridge.conf">
00048          <configObject name="global">
00049             <synopsis>Unused, but reserved.</synopsis>
00050          </configObject>
00051          <configObject name="user_profile">
00052             <synopsis>A named profile to apply to specific callers.</synopsis>
00053             <description><para>Callers in a ConfBridge have a profile associated with them
00054             that determine their options. A configuration section is determined to be a
00055             user_profile when the <literal>type</literal> parameter has a value
00056             of <literal>user</literal>.
00057             </para></description>
00058             <configOption name="type">
00059                <synopsis>Define this configuration category as a user profile.</synopsis>
00060                <description><para>The type parameter determines how a context in the
00061                configuration file is interpreted.</para>
00062                <enumlist>
00063                   <enum name="user"><para>Configure the context as a <replaceable>user_profile</replaceable></para></enum>
00064                   <enum name="bridge"><para>Configure the context as a <replaceable>bridge_profile</replaceable></para></enum>
00065                   <enum name="menu"><para>Configure the context as a <replaceable>menu</replaceable></para></enum>
00066                </enumlist>
00067                </description>
00068             </configOption>
00069             <configOption name="admin">
00070                <synopsis>Sets if the user is an admin or not</synopsis>
00071             </configOption>
00072             <configOption name="marked">
00073                <synopsis>Sets if this is a marked user or not</synopsis>
00074             </configOption>
00075             <configOption name="startmuted">
00076                <synopsis>Sets if all users should start out muted</synopsis>
00077             </configOption>
00078             <configOption name="music_on_hold_when_empty">
00079                <synopsis>Play MOH when user is alone or waiting on a marked user</synopsis>
00080             </configOption>
00081             <configOption name="quiet">
00082                <synopsis>Silence enter/leave prompts and user intros for this user</synopsis>
00083             </configOption>
00084             <configOption name="announce_user_count">
00085                <synopsis>Sets if the number of users should be announced to the user</synopsis>
00086             </configOption>
00087             <configOption name="announce_user_count_all">
00088                <synopsis>Announce user count to all the other users when this user joins</synopsis>
00089                <description><para>Sets if the number of users should be announced to all the other users
00090                in the conference when this user joins. This option can be either set to 'yes' or
00091                a number. When set to a number, the announcement will only occur once the user
00092                count is above the specified number.
00093                </para></description>
00094             </configOption>
00095             <configOption name="announce_only_user">
00096                <synopsis>Announce to a user when they join an empty conference</synopsis>
00097             </configOption>
00098             <configOption name="wait_marked">
00099                <synopsis>Sets if the user must wait for a marked user to enter before joining a conference</synopsis>
00100             </configOption>
00101             <configOption name="end_marked">
00102                <synopsis>Kick the user from the conference when the last marked user leaves</synopsis>
00103             </configOption>
00104             <configOption name="talk_detection_events">
00105                <synopsis>Set whether or not notifications of when a user begins and ends talking should be sent out as events over AMI</synopsis>
00106             </configOption>
00107             <configOption name="dtmf_passthrough">
00108                <synopsis>Sets whether or not DTMF should pass through the conference</synopsis>
00109             </configOption>
00110             <configOption name="announce_join_leave">
00111                <synopsis>Prompt user for their name when joining a conference and play it to the conference when they enter</synopsis>
00112             </configOption>
00113             <configOption name="announce_join_leave_review">
00114                <synopsis>Prompt user for their name when joining a conference and play it to the conference when they enter.
00115                The user will be asked to review the recording of their name before entering the conference.</synopsis>
00116             </configOption>
00117             <configOption name="pin">
00118                <synopsis>Sets a PIN the user must enter before joining the conference</synopsis>
00119             </configOption>
00120             <configOption name="music_on_hold_class">
00121                <synopsis>The MOH class to use for this user</synopsis>
00122             </configOption>
00123             <configOption name="announcement">
00124                <synopsis>Sound file to play to the user when they join a conference</synopsis>
00125             </configOption>
00126             <configOption name="denoise">
00127                <synopsis>Apply a denoise filter to the audio before mixing</synopsis>
00128                <description><para>Sets whether or not a denoise filter should be applied
00129                to the audio before mixing or not.  Off by default. Requires
00130                <literal>codec_speex</literal> to be built and installed.  Do not confuse this option
00131                with <replaceable>drop_silence</replaceable>.  Denoise is useful if there is a lot of background
00132                noise for a user as it attempts to remove the noise while preserving
00133                the speech.  This option does NOT remove silence from being mixed into
00134                the conference and does come at the cost of a slight performance hit.
00135                </para></description>
00136             </configOption>
00137             <configOption name="dsp_drop_silence">
00138                <synopsis>Drop what Asterisk detects as silence from audio sent to the bridge</synopsis>
00139                <description><para>
00140                This option drops what Asterisk detects as silence from
00141                entering into the bridge.  Enabling this option will drastically
00142                improve performance and help remove the buildup of background
00143                noise from the conference. Highly recommended for large conferences
00144                due to its performance enhancements.
00145                </para></description>
00146             </configOption>
00147             <configOption name="dsp_silence_threshold">
00148                <synopsis>The number of milliseconds of detected silence necessary to trigger silence detection</synopsis>
00149                <description><para>
00150                The time in milliseconds of sound falling within the what
00151                the dsp has established as baseline silence before a user
00152                is considered be silent.  This value affects several
00153                operations and should not be changed unless the impact
00154                on call quality is fully understood.</para>
00155                <para>What this value affects internally:</para>
00156                <para>
00157                   1. When talk detection AMI events are enabled, this value
00158                   determines when the user has stopped talking after a
00159                   period of talking.  If this value is set too low
00160                   AMI events indicating the user has stopped talking
00161                   may get falsely sent out when the user briefly pauses
00162                   during mid sentence.
00163                </para>
00164                <para>
00165                   2. The <replaceable>drop_silence</replaceable> option depends on this value to
00166                   determine when the user's audio should begin to be
00167                   dropped from the conference bridge after the user
00168                   stops talking.  If this value is set too low the user's
00169                   audio stream may sound choppy to the other participants.
00170                   This is caused by the user transitioning constantly from
00171                   silence to talking during mid sentence.
00172                </para>
00173                <para>
00174                   The best way to approach this option is to set it slightly above
00175                   the maximum amount of ms of silence a user may generate during
00176                   natural speech.
00177                </para>
00178                <para>By default this value is 2500ms. Valid values are 1 through 2^31.</para>
00179                </description>
00180             </configOption>
00181             <configOption name="dsp_talking_threshold">
00182                <synopsis>The number of milliseconds of detected non-silence necessary to triger talk detection</synopsis>
00183                <description><para>
00184                   The time in milliseconds of sound above what the dsp has
00185                   established as base line silence for a user before a user
00186                   is considered to be talking.  This value affects several
00187                   operations and should not be changed unless the impact on
00188                   call quality is fully understood.</para>
00189                   <para>
00190                   What this value affects internally:
00191                   </para>
00192                   <para>
00193                   1. Audio is only mixed out of a user's incoming audio stream
00194                   if talking is detected.  If this value is set too
00195                   loose the user will hear themselves briefly each
00196                   time they begin talking until the dsp has time to
00197                   establish that they are in fact talking.
00198                   </para>
00199                   <para>
00200                   2. When talk detection AMI events are enabled, this value
00201                   determines when talking has begun which results in
00202                   an AMI event to fire.  If this value is set too tight
00203                   AMI events may be falsely triggered by variants in
00204                   room noise.
00205                   </para>
00206                   <para>
00207                   3. The <replaceable>drop_silence</replaceable> option depends on this value to determine
00208                   when the user's audio should be mixed into the bridge
00209                   after periods of silence.  If this value is too loose
00210                   the beginning of a user's speech will get cut off as they
00211                   transition from silence to talking.
00212                   </para>
00213                   <para>By default this value is 160 ms. Valid values are 1 through 2^31</para>
00214                </description>
00215             </configOption>
00216             <configOption name="jitterbuffer">
00217                <synopsis>Place a jitter buffer on the user's audio stream before audio mixing is performed</synopsis>
00218                <description><para>
00219                   Enabling this option places a jitterbuffer on the user's audio stream
00220                   before audio mixing is performed.  This is highly recommended but will
00221                   add a slight delay to the audio.  This option is using the <literal>JITTERBUFFER</literal>
00222                   dialplan function's default adaptive jitterbuffer.  For a more fine tuned
00223                   jitterbuffer, disable this option and use the <literal>JITTERBUFFER</literal> dialplan function
00224                   on the user before entering the ConfBridge application.
00225                </para></description>
00226             </configOption>
00227             <configOption name="template">
00228                <synopsis>When using the CONFBRIDGE dialplan function, use a user profile as a template for creating a new temporary profile</synopsis>
00229             </configOption>
00230          </configObject>
00231          <configObject name="bridge_profile">
00232             <synopsis>A named profile to apply to specific bridges.</synopsis>
00233             <description><para>ConfBridge bridges have a profile associated with them
00234             that determine their options. A configuration section is determined to be a
00235             <literal>bridge_profile</literal> when the <literal>type</literal> parameter has a value
00236             of <literal>bridge</literal>.
00237             </para></description>
00238             <configOption name="type">
00239                <synopsis>Define this configuration category as a bridge profile</synopsis>
00240                <description><para>The type parameter determines how a context in the
00241                configuration file is interpreted.</para>
00242                <enumlist>
00243                   <enum name="user"><para>Configure the context as a <replaceable>user_profile</replaceable></para></enum>
00244                   <enum name="bridge"><para>Configure the context as a <replaceable>bridge_profile</replaceable></para></enum>
00245                   <enum name="menu"><para>Configure the context as a <replaceable>menu</replaceable></para></enum>
00246                </enumlist>
00247                </description>
00248             </configOption>
00249             <configOption name="jitterbuffer">
00250                <synopsis>Place a jitter buffer on the conference's audio stream</synopsis>
00251             </configOption>
00252             <configOption name="internal_sample_rate">
00253                <synopsis>Set the internal native sample rate for mixing the conference</synopsis>
00254                <description><para>
00255                   Sets the internal native sample rate the
00256                   conference is mixed at.  This is set to automatically
00257                   adjust the sample rate to the best quality by default.
00258                   Other values can be anything from 8000-192000.  If a
00259                   sample rate is set that Asterisk does not support, the
00260                   closest sample rate Asterisk does support to the one requested
00261                   will be used.
00262                </para></description>
00263             </configOption>
00264             <configOption name="language" default="en">
00265                <synopsis>The language used for announcements to the conference.</synopsis>
00266                <description><para>
00267                   By default, announcements to a conference use English.  Which means
00268                   the prompts played to all users within the conference will be
00269                   English.  By changing the language of a bridge, this will change
00270                   the language of the prompts played to all users.
00271                </para></description>
00272             </configOption>
00273             <configOption name="mixing_interval">
00274                <synopsis>Sets the internal mixing interval in milliseconds for the bridge</synopsis>
00275                <description><para>
00276                   Sets the internal mixing interval in milliseconds for the bridge.  This
00277                   number reflects how tight or loose the mixing will be for the conference.
00278                   In order to improve performance a larger mixing interval such as 40ms may
00279                   be chosen.  Using a larger mixing interval comes at the cost of introducing
00280                   larger amounts of delay into the bridge.  Valid values here are 10, 20, 40,
00281                   or 80.
00282                </para></description>
00283             </configOption>
00284             <configOption name="record_conference">
00285                <synopsis>Record the conference starting with the first active user's entrance and ending with the last active user's exit</synopsis>
00286                <description><para>
00287                   Records the conference call starting when the first user
00288                   enters the room, and ending when the last user exits the room.
00289                   The default recorded filename is
00290                   <filename>'confbridge-${name of conference bridge}-${start time}.wav'</filename>
00291                   and the default format is 8khz slinear.  This file will be
00292                   located in the configured monitoring directory in <filename>asterisk.conf</filename>.
00293                </para></description>
00294             </configOption>
00295             <configOption name="record_file" default="confbridge-${name of conference bridge}-${start time}.wav">
00296                <synopsis>The filename of the conference recording</synopsis>
00297                <description><para>
00298                   When <replaceable>record_conference</replaceable> is set to yes, the specific name of the
00299                   record file can be set using this option.  Note that since multiple
00300                   conferences may use the same bridge profile, this may cause issues
00301                   depending on the configuration.  It is recommended to only use this
00302                   option dynamically with the <literal>CONFBRIDGE()</literal> dialplan function. This
00303                   allows the record name to be specified and a unique name to be chosen.
00304                   By default, the record_file is stored in Asterisk's spool/monitor directory
00305                   with a unique filename starting with the 'confbridge' prefix.
00306                </para></description>
00307             </configOption>
00308             <configOption name="record_file_append" default="yes">
00309                <synopsis>Append to record file when starting/stopping on same conference recording</synopsis>
00310                <description><para>
00311                   When <replaceable>record_file_append</replaceable> is set to yes, stopping and starting recording on a
00312                   conference adds the new portion to end of current record_file. When this is
00313                   set to no, a new <replaceable>record_file</replaceable> is generated every time you start then stop recording
00314                   on a conference.
00315                </para></description>
00316             </configOption>
00317             <configOption name="record_file_timestamp" default="yes">
00318                <synopsis>Append the start time to the record_file name so that it is unique.</synopsis>
00319                <description><para>
00320                   When <replaceable>record_file_timestamp</replaceable> is set to yes, the start time is appended to
00321                   <replaceable>record_file</replaceable> so that the filename is unique. This allows you to specify
00322                   a <replaceable>record_file</replaceable> but not overwrite existing recordings.
00323                </para></description>
00324             </configOption>
00325             <configOption name="record_options" default="">
00326                <synopsis>Pass additional options to MixMonitor when recording</synopsis>
00327                <description><para>
00328                   Pass additional options to MixMonitor when <replaceable>record_conference</replaceable> is set to yes.
00329                   See <literal>MixMonitor</literal> for available options.
00330                </para></description>
00331             </configOption>
00332             <configOption name="record_command" default="">
00333                <synopsis>Execute a command after recording ends</synopsis>
00334                <description><para>
00335                   Executes the specified command when recording ends. Any strings matching <literal>^{X}</literal> will be
00336                   unescaped to <variable>X</variable>. All variables will be evaluated at the time ConfBridge is called.
00337                </para></description>
00338             </configOption>
00339             <configOption name="video_mode">
00340                <synopsis>Sets how confbridge handles video distribution to the conference participants</synopsis>
00341                <description><para>
00342                   Sets how confbridge handles video distribution to the conference participants.
00343                   Note that participants wanting to view and be the source of a video feed
00344                   <emphasis>MUST</emphasis> be sharing the same video codec.  Also, using video in conjunction with
00345                   with the jitterbuffer currently results in the audio being slightly out of sync
00346                   with the video.  This is a result of the jitterbuffer only working on the audio
00347                   stream.  It is recommended to disable the jitterbuffer when video is used.</para>
00348                   <enumlist>
00349                      <enum name="none">
00350                         <para>No video sources are set by default in the conference. It is still
00351                         possible for a user to be set as a video source via AMI or DTMF action
00352                         at any time.</para>
00353                      </enum>
00354                      <enum name="follow_talker">
00355                         <para>The video feed will follow whoever is talking and providing video.</para>
00356                      </enum>
00357                      <enum name="last_marked">
00358                         <para>The last marked user to join the conference with video capabilities
00359                         will be the single source of video distributed to all participants.
00360                         If multiple marked users are capable of video, the last one to join
00361                         is always the source, when that user leaves it goes to the one who
00362                         joined before them.</para>
00363                      </enum>
00364                      <enum name="first_marked">
00365                         <para>The first marked user to join the conference with video capabilities
00366                         is the single source of video distribution among all participants. If
00367                         that user leaves, the marked user to join after them becomes the source.</para>
00368                      </enum>
00369                   </enumlist>
00370                </description>
00371             </configOption>
00372             <configOption name="max_members">
00373                <synopsis>Limit the maximum number of participants for a single conference</synopsis>
00374                <description><para>
00375                   This option limits the number of participants for a single
00376                   conference to a specific number.  By default conferences
00377                   have no participant limit. After the limit is reached, the
00378                   conference will be locked until someone leaves.  Note however
00379                   that an Admin user will always be alowed to join the conference
00380                   regardless if this limit is reached or not.
00381                </para></description>
00382             </configOption>
00383             <configOption name="^sound_">
00384                <synopsis>Override the various conference bridge sound files</synopsis>
00385                <description><para>
00386                   All sounds in the conference are customizable using the bridge profile options below.
00387                   Simply state the option followed by the filename or full path of the filename after
00388                   the option.  Example: <literal>sound_had_joined=conf-hasjoin</literal>  This will play the <literal>conf-hasjoin</literal>
00389                   sound file found in the sounds directory when announcing someone's name is joining the
00390                   conference.</para>
00391                   <enumlist>
00392                      <enum name="sound_join"><para>The sound played to everyone when someone enters the conference.</para></enum>
00393                      <enum name="sound_leave"><para>The sound played to everyone when someone leaves the conference.</para></enum>
00394                      <enum name="sound_has_joined"><para>The sound played before announcing someone's name has
00395                               joined the conference. This is used for user intros.
00396                               Example <literal>"_____ has joined the conference"</literal></para></enum>
00397                      <enum name="sound_has_left"><para>The sound played when announcing someone's name has
00398                               left the conference. This is used for user intros.
00399                               Example <literal>"_____ has left the conference"</literal></para></enum>
00400                      <enum name="sound_kicked"><para>The sound played to a user who has been kicked from the conference.</para></enum>
00401                      <enum name="sound_muted"><para>The sound played when the mute option it toggled on.</para></enum>
00402                      <enum name="sound_unmuted"><para>The sound played when the mute option it toggled off.</para></enum>
00403                      <enum name="sound_only_person"><para>The sound played when the user is the only person in the conference.</para></enum>
00404                      <enum name="sound_only_one"><para>The sound played to a user when there is only one other
00405                               person is in the conference.</para></enum>
00406                      <enum name="sound_there_are"><para>The sound played when announcing how many users there
00407                               are in a conference.</para></enum>
00408                      <enum name="sound_other_in_party"><para>This file is used in conjunction with <literal>sound_there_are</literal>
00409                               when announcing how many users there are in the conference.
00410                               The sounds are stringed together like this.
00411                               <literal>"sound_there_are" ${number of participants} "sound_other_in_party"</literal></para></enum>
00412                      <enum name="sound_place_into_conference"><para>The sound played when someone is placed into the conference
00413                               after waiting for a marked user.</para></enum>
00414                      <enum name="sound_wait_for_leader"><para>The sound played when a user is placed into a conference that
00415                               can not start until a marked user enters.</para></enum>
00416                      <enum name="sound_leader_has_left"><para>The sound played when the last marked user leaves the conference.</para></enum>
00417                      <enum name="sound_get_pin"><para>The sound played when prompting for a conference pin number.</para></enum>
00418                      <enum name="sound_invalid_pin"><para>The sound played when an invalid pin is entered too many times.</para></enum>
00419                      <enum name="sound_locked"><para>The sound played to a user trying to join a locked conference.</para></enum>
00420                      <enum name="sound_locked_now"><para>The sound played to an admin after toggling the conference to locked mode.</para></enum>
00421                      <enum name="sound_unlocked_now"><para>The sound played to an admin after toggling the conference to unlocked mode.</para></enum>
00422                      <enum name="sound_error_menu"><para>The sound played when an invalid menu option is entered.</para></enum>
00423                   </enumlist>
00424                </description>
00425             </configOption>
00426             <configOption name="template">
00427                <synopsis>When using the CONFBRIDGE dialplan function, use a bridge profile as a template for creating a new temporary profile</synopsis>
00428             </configOption>
00429          </configObject>
00430          <configObject name="menu">
00431             <synopsis>A conference user menu</synopsis>
00432             <description>
00433                <para>Conference users, as defined by a <replaceable>conf_user</replaceable>,
00434                can have a DTMF menu assigned to their profile when they enter the
00435                <literal>ConfBridge</literal> application.</para>
00436             </description>
00437             <configOption name="type">
00438                <synopsis>Define this configuration category as a menu</synopsis>
00439                <description><para>The type parameter determines how a context in the
00440                configuration file is interpreted.</para>
00441                <enumlist>
00442                   <enum name="user"><para>Configure the context as a <replaceable>user_profile</replaceable></para></enum>
00443                   <enum name="bridge"><para>Configure the context as a <replaceable>bridge_profile</replaceable></para></enum>
00444                   <enum name="menu"><para>Configure the context as a <replaceable>menu</replaceable></para></enum>
00445                </enumlist>
00446                </description>
00447             </configOption>
00448             <configOption name="template">
00449                <synopsis>When using the CONFBRIDGE dialplan function, use a menu profile as a template for creating a new temporary profile</synopsis>
00450             </configOption>
00451             <configOption name="^[0-9A-D*#]+$">
00452                <synopsis>DTMF sequences to assign various confbridge actions to</synopsis>
00453                <description>
00454                <para>The ConfBridge application also has the ability to apply custom DTMF menus to
00455                each channel using the application.  Like the User and Bridge profiles a menu
00456                is passed in to ConfBridge as an argument in the dialplan.</para>
00457                <para>Below is a list of menu actions that can be assigned to a DTMF sequence.</para>
00458                <note><para>
00459                   To have the first DTMF digit in a sequence be the '#' character, you need to
00460                   escape it.  If it is not escaped then normal config file processing will
00461                   think it is a directive like #include.  For example: The mute setting is
00462                   toggled when <literal>#1</literal> is pressed.</para>
00463                   <para><literal>\#1=toggle_mute</literal></para>
00464                </note>
00465                <note><para>
00466                A single DTMF sequence can have multiple actions associated with it. This is
00467                accomplished by stringing the actions together and using a <literal>,</literal> as the
00468                delimiter.  Example:  Both listening and talking volume is reset when <literal>5</literal> is
00469                pressed.  <literal>5=reset_talking_volume, reset_listening_volume</literal></para></note>
00470                <enumlist>
00471                   <enum name="playback(filename&amp;filename2&amp;...)"><para>
00472                      <literal>playback</literal> will play back an audio file to a channel
00473                      and then immediately return to the conference.
00474                      This file can not be interupted by DTMF.
00475                      Multiple files can be chained together using the
00476                      <literal>&amp;</literal> character.</para></enum>
00477                   <enum name="playback_and_continue(filename&amp;filename2&amp;...)"><para>
00478                      <literal>playback_and_continue</literal> will
00479                      play back a prompt while continuing to
00480                      collect the dtmf sequence.  This is useful
00481                      when using a menu prompt that describes all
00482                      the menu options.  Note however that any DTMF
00483                      during this action will terminate the prompts
00484                      playback.  Prompt files can be chained together
00485                      using the <literal>&amp;</literal> character as a delimiter.</para></enum>
00486                   <enum name="toggle_mute"><para>
00487                      Toggle turning on and off mute.  Mute will make the user silent
00488                      to everyone else, but the user will still be able to listen in.
00489                      </para></enum>
00490                   <enum name="no_op"><para>
00491                      This action does nothing (No Operation). Its only real purpose exists for
00492                      being able to reserve a sequence in the config as a menu exit sequence.</para></enum>
00493                   <enum name="decrease_listening_volume"><para>
00494                      Decreases the channel's listening volume.</para></enum>
00495                   <enum name="increase_listening_volume"><para>
00496                      Increases the channel's listening volume.</para></enum>
00497                   <enum name="reset_listening_volume"><para>
00498                      Reset channel's listening volume to default level.</para></enum>
00499                   <enum name="decrease_talking_volume"><para>
00500                      Decreases the channel's talking volume.</para></enum>
00501                   <enum name="increase_talking_volume"><para>
00502                      Increases the channel's talking volume.</para></enum>
00503                   <enum name="reset_talking_volume"><para>
00504                      Reset channel's talking volume to default level.</para></enum>
00505                   <enum name="dialplan_exec(context,exten,priority)"><para>
00506                      The <literal>dialplan_exec</literal> action allows a user
00507                      to escape from the conference and execute
00508                      commands in the dialplan.  Once the dialplan
00509                      exits the user will be put back into the
00510                      conference.  The possibilities are endless!</para></enum>
00511                   <enum name="leave_conference"><para>
00512                      This action allows a user to exit the conference and continue
00513                      execution in the dialplan.</para></enum>
00514                   <enum name="admin_kick_last"><para>
00515                      This action allows an Admin to kick the last participant from the
00516                      conference. This action will only work for admins which allows
00517                      a single menu to be used for both users and admins.</para></enum>
00518                   <enum name="admin_toggle_conference_lock"><para>
00519                      This action allows an Admin to toggle locking and
00520                      unlocking the conference.  Non admins can not use
00521                      this action even if it is in their menu.</para></enum>
00522                   <enum name="set_as_single_video_src"><para>
00523                      This action allows any user to set themselves as the
00524                      single video source distributed to all participants.
00525                      This will make the video feed stick to them regardless
00526                      of what the <literal>video_mode</literal> is set to.</para></enum>
00527                   <enum name="release_as_single_video_src"><para>
00528                      This action allows a user to release themselves as
00529                      the video source.  If <literal>video_mode</literal> is not set to <literal>none</literal>
00530                      this action will result in the conference returning to
00531                      whatever video mode the bridge profile is using.</para>
00532                      <para>Note that this action will have no effect if the user
00533                      is not currently the video source.  Also, the user is
00534                      not guaranteed by using this action that they will not
00535                      become the video source again.  The bridge will return
00536                      to whatever operation the <literal>video_mode</literal> option is set to
00537                      upon release of the video src.</para></enum>
00538                   <enum name="admin_toggle_mute_participants"><para>
00539                      This action allows an administrator to toggle the mute
00540                      state for all non-admins within a conference.  All
00541                      admin users are unaffected by this option.  Note that all
00542                      users, regardless of their admin status, are notified
00543                      that the conference is muted.</para></enum>
00544                   <enum name="participant_count"><para>
00545                      This action plays back the number of participants currently
00546                      in a conference</para></enum>
00547                   </enumlist>
00548                </description>
00549             </configOption>
00550          </configObject>
00551       </configFile>
00552    </configInfo>
00553 ***/
00554 
00555 struct confbridge_cfg {
00556    struct ao2_container *bridge_profiles;
00557    struct ao2_container *user_profiles;
00558    struct ao2_container *menus;
00559 };
00560 
00561 static int verify_default_profiles(void);
00562 static void *bridge_profile_alloc(const char *category);
00563 static void *bridge_profile_find(struct ao2_container *container, const char *category);
00564 static struct bridge_profile_sounds *bridge_profile_sounds_alloc(void);
00565 
00566 static void bridge_profile_destructor(void *obj)
00567 {
00568    struct bridge_profile *b_profile = obj;
00569    ao2_cleanup(b_profile->sounds);
00570 }
00571 
00572 static void *bridge_profile_alloc(const char *category)
00573 {
00574    struct bridge_profile *b_profile;
00575 
00576    if (!(b_profile = ao2_alloc(sizeof(*b_profile), bridge_profile_destructor))) {
00577       return NULL;
00578    }
00579 
00580    if (!(b_profile->sounds = bridge_profile_sounds_alloc())) {
00581       ao2_ref(b_profile, -1);
00582       return NULL;
00583    }
00584 
00585    ast_copy_string(b_profile->name, category, sizeof(b_profile->name));
00586 
00587    return b_profile;
00588 }
00589 
00590 static void *bridge_profile_find(struct ao2_container *container, const char *category)
00591 {
00592    return ao2_find(container, category, OBJ_KEY);
00593 }
00594 
00595 static struct aco_type bridge_type = {
00596    .type = ACO_ITEM,
00597    .name = "bridge_profile",
00598    .category_match = ACO_BLACKLIST,
00599    .category = "^general$",
00600    .matchfield = "type",
00601    .matchvalue = "bridge",
00602    .item_alloc = bridge_profile_alloc,
00603    .item_find = bridge_profile_find,
00604    .item_offset = offsetof(struct confbridge_cfg, bridge_profiles),
00605 };
00606 
00607 static void *user_profile_alloc(const char *category);
00608 static void *user_profile_find(struct ao2_container *container, const char *category);
00609 static void user_profile_destructor(void *obj)
00610 {
00611    return;
00612 }
00613 
00614 static void *user_profile_alloc(const char *category)
00615 {
00616    struct user_profile *u_profile;
00617 
00618    if (!(u_profile = ao2_alloc(sizeof(*u_profile), user_profile_destructor))) {
00619       return NULL;
00620    }
00621 
00622    ast_copy_string(u_profile->name, category, sizeof(u_profile->name));
00623 
00624    return u_profile;
00625 }
00626 
00627 static void *user_profile_find(struct ao2_container *container, const char *category)
00628 {
00629    return ao2_find(container, category, OBJ_KEY);
00630 }
00631 
00632 static struct aco_type user_type = {
00633    .type = ACO_ITEM,
00634    .name  = "user_profile",
00635    .category_match = ACO_BLACKLIST,
00636    .category = "^general$",
00637    .matchfield = "type",
00638    .matchvalue = "user",
00639    .item_alloc = user_profile_alloc,
00640    .item_find = user_profile_find,
00641    .item_offset = offsetof(struct confbridge_cfg, user_profiles),
00642 };
00643 
00644 static void *menu_alloc(const char *category);
00645 static void *menu_find(struct ao2_container *container, const char *category);
00646 static void menu_destructor(void *obj);
00647 
00648 static void *menu_alloc(const char *category)
00649 {
00650    struct conf_menu *menu;
00651    if (!(menu = ao2_alloc(sizeof(*menu), menu_destructor))) {
00652       return NULL;
00653    }
00654    ast_copy_string(menu->name, category, sizeof(menu->name));
00655    return menu;
00656 }
00657 
00658 static void *menu_find(struct ao2_container *container, const char *category)
00659 {
00660    return ao2_find(container, category, OBJ_KEY);
00661 }
00662 
00663 static struct aco_type menu_type = {
00664    .type = ACO_ITEM,
00665    .name = "menu",
00666    .category_match = ACO_BLACKLIST,
00667    .category = "^general$",
00668    .matchfield = "type",
00669    .matchvalue = "menu",
00670    .item_alloc = menu_alloc,
00671    .item_find = menu_find,
00672    .item_offset = offsetof(struct confbridge_cfg, menus),
00673 };
00674 
00675 /* Used to pass to aco_option_register */
00676 static struct aco_type *bridge_types[] = ACO_TYPES(&bridge_type);
00677 static struct aco_type *menu_types[] = ACO_TYPES(&menu_type);
00678 static struct aco_type *user_types[] = ACO_TYPES(&user_type);
00679 
00680 /* The general category is reserved, but unused */
00681 static struct aco_type general_type = {
00682    .type = ACO_GLOBAL,
00683    .name = "global",
00684    .category_match = ACO_WHITELIST,
00685    .category = "^general$",
00686 };
00687 
00688 static struct aco_file confbridge_conf = {
00689    .filename = "confbridge.conf",
00690    .types = ACO_TYPES(&bridge_type, &user_type, &menu_type, &general_type),
00691 };
00692 
00693 static AO2_GLOBAL_OBJ_STATIC(cfg_handle);
00694 
00695 static void *confbridge_cfg_alloc(void);
00696 
00697 CONFIG_INFO_STANDARD(cfg_info, cfg_handle, confbridge_cfg_alloc,
00698    .files = ACO_FILES(&confbridge_conf),
00699    .pre_apply_config = verify_default_profiles,
00700 );
00701 
00702 /*! bridge profile container functions */
00703 static int bridge_cmp_cb(void *obj, void *arg, int flags)
00704 {
00705    const struct bridge_profile *left = obj;
00706    const struct bridge_profile *right = arg;
00707    const char *right_name = arg;
00708    int cmp;
00709 
00710    switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00711    default:
00712    case OBJ_POINTER:
00713       right_name = right->name;
00714       /* Fall through */
00715    case OBJ_KEY:
00716       cmp = strcasecmp(left->name, right_name);
00717       break;
00718    case OBJ_PARTIAL_KEY:
00719       cmp = strncasecmp(left->name, right_name, strlen(right_name));
00720       break;
00721    }
00722    return cmp ? 0 : CMP_MATCH;
00723 }
00724 
00725 static int bridge_hash_cb(const void *obj, const int flags)
00726 {
00727    const struct bridge_profile *b_profile = obj;
00728    const char *name = obj;
00729    int hash;
00730 
00731    switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00732    default:
00733    case OBJ_POINTER:
00734       name = b_profile->name;
00735       /* Fall through */
00736    case OBJ_KEY:
00737       hash = ast_str_case_hash(name);
00738       break;
00739    case OBJ_PARTIAL_KEY:
00740       /* Should never happen in hash callback. */
00741       ast_assert(0);
00742       hash = 0;
00743       break;
00744    }
00745    return hash;
00746 }
00747 
00748 /*! menu container functions */
00749 static int menu_cmp_cb(void *obj, void *arg, int flags)
00750 {
00751    const struct conf_menu *left = obj;
00752    const struct conf_menu *right = arg;
00753    const char *right_name = arg;
00754    int cmp;
00755 
00756    switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00757    default:
00758    case OBJ_POINTER:
00759       right_name = right->name;
00760       /* Fall through */
00761    case OBJ_KEY:
00762       cmp = strcasecmp(left->name, right_name);
00763       break;
00764    case OBJ_PARTIAL_KEY:
00765       cmp = strncasecmp(left->name, right_name, strlen(right_name));
00766       break;
00767    }
00768    return cmp ? 0 : CMP_MATCH;
00769 }
00770 
00771 static int menu_hash_cb(const void *obj, const int flags)
00772 {
00773    const struct conf_menu *menu = obj;
00774    const char *name = obj;
00775    int hash;
00776 
00777    switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00778    default:
00779    case OBJ_POINTER:
00780       name = menu->name;
00781       /* Fall through */
00782    case OBJ_KEY:
00783       hash = ast_str_case_hash(name);
00784       break;
00785    case OBJ_PARTIAL_KEY:
00786       /* Should never happen in hash callback. */
00787       ast_assert(0);
00788       hash = 0;
00789       break;
00790    }
00791    return hash;
00792 }
00793 
00794 static void menu_destructor(void *obj)
00795 {
00796    struct conf_menu *menu = obj;
00797    struct conf_menu_entry *entry = NULL;
00798 
00799    while ((entry = AST_LIST_REMOVE_HEAD(&menu->entries, entry))) {
00800       conf_menu_entry_destroy(entry);
00801       ast_free(entry);
00802    }
00803 }
00804 
00805 /*! User profile container functions */
00806 static int user_cmp_cb(void *obj, void *arg, int flags)
00807 {
00808    const struct user_profile *left = obj;
00809    const struct user_profile *right = arg;
00810    const char *right_name = arg;
00811    int cmp;
00812 
00813    switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00814    default:
00815    case OBJ_POINTER:
00816       right_name = right->name;
00817       /* Fall through */
00818    case OBJ_KEY:
00819       cmp = strcasecmp(left->name, right_name);
00820       break;
00821    case OBJ_PARTIAL_KEY:
00822       cmp = strncasecmp(left->name, right_name, strlen(right_name));
00823       break;
00824    }
00825    return cmp ? 0 : CMP_MATCH;
00826 }
00827 
00828 static int user_hash_cb(const void *obj, const int flags)
00829 {
00830    const struct user_profile *u_profile = obj;
00831    const char *name = obj;
00832    int hash;
00833 
00834    switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00835    default:
00836    case OBJ_POINTER:
00837       name = u_profile->name;
00838       /* Fall through */
00839    case OBJ_KEY:
00840       hash = ast_str_case_hash(name);
00841       break;
00842    case OBJ_PARTIAL_KEY:
00843       /* Should never happen in hash callback. */
00844       ast_assert(0);
00845       hash = 0;
00846       break;
00847    }
00848    return hash;
00849 }
00850 
00851 /*! Bridge Profile Sounds functions */
00852 static void bridge_profile_sounds_destroy_cb(void *obj)
00853 {
00854    struct bridge_profile_sounds *sounds = obj;
00855    ast_string_field_free_memory(sounds);
00856 }
00857 
00858 static struct bridge_profile_sounds *bridge_profile_sounds_alloc(void)
00859 {
00860    struct bridge_profile_sounds *sounds = ao2_alloc(sizeof(*sounds), bridge_profile_sounds_destroy_cb);
00861 
00862    if (!sounds) {
00863       return NULL;
00864    }
00865    if (ast_string_field_init(sounds, 512)) {
00866       ao2_ref(sounds, -1);
00867       return NULL;
00868    }
00869 
00870    return sounds;
00871 }
00872 
00873 static int set_sound(const char *sound_name, const char *sound_file, struct bridge_profile *b_profile)
00874 {
00875    struct bridge_profile_sounds *sounds = b_profile->sounds;
00876    if (ast_strlen_zero(sound_file)) {
00877       return -1;
00878    }
00879 
00880    if (!strcasecmp(sound_name, "sound_only_person")) {
00881       ast_string_field_set(sounds, onlyperson, sound_file);
00882    } else if (!strcasecmp(sound_name, "sound_only_one")) {
00883       ast_string_field_set(sounds, onlyone, sound_file);
00884    } else if (!strcasecmp(sound_name, "sound_has_joined")) {
00885       ast_string_field_set(sounds, hasjoin, sound_file);
00886    } else if (!strcasecmp(sound_name, "sound_has_left")) {
00887       ast_string_field_set(sounds, hasleft, sound_file);
00888    } else if (!strcasecmp(sound_name, "sound_kicked")) {
00889       ast_string_field_set(sounds, kicked, sound_file);
00890    } else if (!strcasecmp(sound_name, "sound_muted")) {
00891       ast_string_field_set(sounds, muted, sound_file);
00892    } else if (!strcasecmp(sound_name, "sound_unmuted")) {
00893       ast_string_field_set(sounds, unmuted, sound_file);
00894    } else if (!strcasecmp(sound_name, "sound_there_are")) {
00895       ast_string_field_set(sounds, thereare, sound_file);
00896    } else if (!strcasecmp(sound_name, "sound_other_in_party")) {
00897       ast_string_field_set(sounds, otherinparty, sound_file);
00898    } else if (!strcasecmp(sound_name, "sound_place_into_conference")) {
00899       static int deprecation_warning = 1;
00900       if (deprecation_warning) {
00901          ast_log(LOG_WARNING, "sound_place_into_conference is deprecated"
00902             " and unused. Use sound_begin for similar functionality.");
00903          deprecation_warning = 0;
00904       }
00905       ast_string_field_set(sounds, placeintoconf, sound_file);
00906    } else if (!strcasecmp(sound_name, "sound_wait_for_leader")) {
00907       ast_string_field_set(sounds, waitforleader, sound_file);
00908    } else if (!strcasecmp(sound_name, "sound_leader_has_left")) {
00909       ast_string_field_set(sounds, leaderhasleft, sound_file);
00910    } else if (!strcasecmp(sound_name, "sound_get_pin")) {
00911       ast_string_field_set(sounds, getpin, sound_file);
00912    } else if (!strcasecmp(sound_name, "sound_invalid_pin")) {
00913       ast_string_field_set(sounds, invalidpin, sound_file);
00914    } else if (!strcasecmp(sound_name, "sound_locked")) {
00915       ast_string_field_set(sounds, locked, sound_file);
00916    } else if (!strcasecmp(sound_name, "sound_unlocked_now")) {
00917       ast_string_field_set(sounds, unlockednow, sound_file);
00918    } else if (!strcasecmp(sound_name, "sound_locked_now")) {
00919       ast_string_field_set(sounds, lockednow, sound_file);
00920    } else if (!strcasecmp(sound_name, "sound_error_menu")) {
00921       ast_string_field_set(sounds, errormenu, sound_file);
00922    } else if (!strcasecmp(sound_name, "sound_join")) {
00923       ast_string_field_set(sounds, join, sound_file);
00924    } else if (!strcasecmp(sound_name, "sound_leave")) {
00925       ast_string_field_set(sounds, leave, sound_file);
00926    } else if (!strcasecmp(sound_name, "sound_participants_muted")) {
00927       ast_string_field_set(sounds, participantsmuted, sound_file);
00928    } else if (!strcasecmp(sound_name, "sound_participants_unmuted")) {
00929       ast_string_field_set(sounds, participantsunmuted, sound_file);
00930    } else if (!strcasecmp(sound_name, "sound_begin")) {
00931       ast_string_field_set(sounds, begin, sound_file);
00932    } else {
00933       return -1;
00934    }
00935 
00936    return 0;
00937 }
00938 
00939 /*! CONFBRIDGE dialplan function functions and channel datastore. */
00940 struct func_confbridge_data {
00941    struct bridge_profile b_profile;
00942    struct user_profile u_profile;
00943    struct conf_menu *menu;
00944    unsigned int b_usable:1; /*!< Tells if bridge profile is usable or not */
00945    unsigned int u_usable:1; /*!< Tells if user profile is usable or not */
00946    unsigned int m_usable:1; /*!< Tells if menu profile is usable or not */
00947 };
00948 
00949 static void func_confbridge_data_destructor(struct func_confbridge_data *b_data)
00950 {
00951    conf_bridge_profile_destroy(&b_data->b_profile);
00952    ao2_cleanup(b_data->menu);
00953    ast_free(b_data);
00954 }
00955 
00956 static void func_confbridge_destroy_cb(void *data)
00957 {
00958    struct func_confbridge_data *b_data = data;
00959    func_confbridge_data_destructor(b_data);
00960 };
00961 
00962 static const struct ast_datastore_info confbridge_datastore = {
00963    .type = "confbridge",
00964    .destroy = func_confbridge_destroy_cb
00965 };
00966 int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00967 {
00968    struct ast_datastore *datastore;
00969    struct func_confbridge_data *b_data;
00970    char *parse;
00971    struct ast_variable tmpvar = { 0, };
00972    AST_DECLARE_APP_ARGS(args,
00973       AST_APP_ARG(type);
00974       AST_APP_ARG(option);
00975    );
00976 
00977    if (!chan) {
00978       ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
00979       return -1;
00980    }
00981 
00982    /* parse all the required arguments and make sure they exist. */
00983    if (ast_strlen_zero(data)) {
00984       return -1;
00985    }
00986    parse = ast_strdupa(data);
00987    AST_STANDARD_APP_ARGS(args, parse);
00988    if (ast_strlen_zero(args.type) || ast_strlen_zero(args.option)) {
00989       return -1;
00990    }
00991 
00992    ast_channel_lock(chan);
00993    datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
00994    if (!datastore) {
00995       datastore = ast_datastore_alloc(&confbridge_datastore, NULL);
00996       if (!datastore) {
00997          ast_channel_unlock(chan);
00998          return 0;
00999       }
01000       b_data = ast_calloc(1, sizeof(*b_data));
01001       if (!b_data) {
01002          ast_channel_unlock(chan);
01003          ast_datastore_free(datastore);
01004          return 0;
01005       }
01006       datastore->data = b_data;
01007       b_data->b_profile.sounds = bridge_profile_sounds_alloc();
01008       if (!b_data->b_profile.sounds) {
01009          ast_channel_unlock(chan);
01010          ast_datastore_free(datastore);
01011          return 0;
01012       }
01013       if (!(b_data->menu = menu_alloc("dialplan"))) {
01014          ast_channel_unlock(chan);
01015          ast_datastore_free(datastore);
01016          return 0;
01017       }
01018       ast_channel_datastore_add(chan, datastore);
01019    } else {
01020       b_data = datastore->data;
01021    }
01022    ast_channel_unlock(chan);
01023 
01024    /* SET(CONFBRIDGE(type,option)=value) */
01025    if (!value) {
01026       value = "";
01027    }
01028    tmpvar.name = args.option;
01029    tmpvar.value = value;
01030    tmpvar.file = "CONFBRIDGE";
01031    if (!strcasecmp(args.type, "bridge")) {
01032       if (!strcasecmp(args.option, "clear")) {
01033          b_data->b_usable = 0;
01034          conf_bridge_profile_destroy(&b_data->b_profile);
01035          memset(&b_data->b_profile, 0, sizeof(b_data->b_profile)) ;
01036          if (!(b_data->b_profile.sounds = bridge_profile_sounds_alloc())) {
01037             /* If this reallocation fails, the datastore has become unusable and must be destroyed. */
01038             ast_channel_lock(chan);
01039             ast_channel_datastore_remove(chan, datastore);
01040             ast_channel_unlock(chan);
01041             ast_datastore_free(datastore);
01042          }
01043          return 0;
01044       } else if (!aco_process_var(&bridge_type, "dialplan", &tmpvar, &b_data->b_profile)) {
01045          b_data->b_usable = 1;
01046          return 0;
01047       }
01048    } else if (!strcasecmp(args.type, "user")) {
01049       if (!strcasecmp(args.option, "clear")) {
01050          b_data->u_usable = 0;
01051          user_profile_destructor(&b_data->u_profile);
01052          memset(&b_data->u_profile, 0, sizeof(b_data->u_profile));
01053          return 0;
01054       } else if (!aco_process_var(&user_type, "dialplan", &tmpvar, &b_data->u_profile)) {
01055          b_data->u_usable = 1;
01056          return 0;
01057       }
01058    } else if (!strcasecmp(args.type, "menu")) {
01059       if (!strcasecmp(args.option, "clear")) {
01060          b_data->m_usable = 0;
01061          ao2_cleanup(b_data->menu);
01062          if (!(b_data->menu = menu_alloc("dialplan"))) {
01063             /* If this reallocation fails, the datastore has become unusable and must be destroyed */
01064             ast_channel_lock(chan);
01065             ast_channel_datastore_remove(chan, datastore);
01066             ast_channel_unlock(chan);
01067             ast_datastore_free(datastore);
01068          }
01069          return 0;
01070       } else if (!aco_process_var(&menu_type, "dialplan", &tmpvar, b_data->menu)) {
01071          b_data->m_usable = 1;
01072          return 0;
01073       }
01074    }
01075 
01076    ast_log(LOG_WARNING, "%s(%s,%s) cannot be set to '%s'. Invalid type, option, or value.\n",
01077       cmd, args.type, args.option, value);
01078    return -1;
01079 }
01080 
01081 static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum conf_menu_action_id id, char *databuf)
01082 {
01083    struct conf_menu_action *menu_action = ast_calloc(1, sizeof(*menu_action));
01084 
01085    if (!menu_action) {
01086       return -1;
01087    }
01088    menu_action->id = id;
01089 
01090    switch (id) {
01091    case MENU_ACTION_NOOP:
01092    case MENU_ACTION_TOGGLE_MUTE:
01093    case MENU_ACTION_INCREASE_LISTENING:
01094    case MENU_ACTION_DECREASE_LISTENING:
01095    case MENU_ACTION_INCREASE_TALKING:
01096    case MENU_ACTION_DECREASE_TALKING:
01097    case MENU_ACTION_RESET_LISTENING:
01098    case MENU_ACTION_RESET_TALKING:
01099    case MENU_ACTION_ADMIN_TOGGLE_LOCK:
01100    case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
01101    case MENU_ACTION_PARTICIPANT_COUNT:
01102    case MENU_ACTION_ADMIN_KICK_LAST:
01103    case MENU_ACTION_LEAVE:
01104    case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
01105    case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
01106       break;
01107    case MENU_ACTION_PLAYBACK:
01108    case MENU_ACTION_PLAYBACK_AND_CONTINUE:
01109       if (!(ast_strlen_zero(databuf))) {
01110          ast_copy_string(menu_action->data.playback_file, databuf, sizeof(menu_action->data.playback_file));
01111       } else {
01112          ast_free(menu_action);
01113          return -1;
01114       }
01115       break;
01116    case MENU_ACTION_DIALPLAN_EXEC:
01117       if (!(ast_strlen_zero(databuf))) {
01118          AST_DECLARE_APP_ARGS(args,
01119             AST_APP_ARG(context);
01120             AST_APP_ARG(exten);
01121             AST_APP_ARG(priority);
01122          );
01123          AST_STANDARD_APP_ARGS(args, databuf);
01124          if (!ast_strlen_zero(args.context)) {
01125             ast_copy_string(menu_action->data.dialplan_args.context,
01126                args.context,
01127                sizeof(menu_action->data.dialplan_args.context));
01128          }
01129          if (!ast_strlen_zero(args.exten)) {
01130             ast_copy_string(menu_action->data.dialplan_args.exten,
01131                args.exten,
01132                sizeof(menu_action->data.dialplan_args.exten));
01133          }
01134          menu_action->data.dialplan_args.priority = 1; /* 1 by default */
01135          if (!ast_strlen_zero(args.priority) &&
01136             (sscanf(args.priority, "%30d", &menu_action->data.dialplan_args.priority) != 1)) {
01137             /* invalid priority */
01138             ast_free(menu_action);
01139             return -1;
01140          }
01141       } else {
01142          ast_free(menu_action);
01143          return -1;
01144       }
01145    };
01146 
01147    AST_LIST_INSERT_TAIL(&menu_entry->actions, menu_action, action);
01148 
01149    return 0;
01150 }
01151 
01152 static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *action_names)
01153 {
01154    struct conf_menu_entry *menu_entry = NULL, *cur = NULL;
01155    int res = 0;
01156    char *tmp_action_names = ast_strdupa(action_names);
01157    char *action = NULL;
01158    char *action_args;
01159    char *tmp;
01160    char buf[PATH_MAX];
01161    char *delimiter = ",";
01162 
01163    if (!(menu_entry = ast_calloc(1, sizeof(*menu_entry)))) {
01164       return -1;
01165    }
01166 
01167    for (;;) {
01168       char *comma;
01169       char *startbrace;
01170       char *endbrace;
01171       unsigned int action_len;
01172 
01173       if (ast_strlen_zero(tmp_action_names)) {
01174          break;
01175       }
01176       startbrace = strchr(tmp_action_names, '(');
01177       endbrace = strchr(tmp_action_names, ')');
01178       comma = strchr(tmp_action_names, ',');
01179 
01180       /* If the next action has brackets with comma delimited arguments in it,
01181        * make the delimeter ')' instead of a comma to preserve the argments */
01182       if (startbrace && endbrace && comma && (comma > startbrace && comma < endbrace)) {
01183          delimiter = ")";
01184       } else {
01185          delimiter = ",";
01186       }
01187 
01188       if (!(action = strsep(&tmp_action_names, delimiter))) {
01189          break;
01190       }
01191 
01192       action = ast_strip(action);
01193       if (ast_strlen_zero(action)) {
01194          continue;
01195       }
01196 
01197       action_len = strlen(action);
01198       ast_copy_string(menu_entry->dtmf, dtmf, sizeof(menu_entry->dtmf));
01199       if (!strcasecmp(action, "toggle_mute")) {
01200          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_TOGGLE_MUTE, NULL);
01201       } else if (!strcasecmp(action, "no_op")) {
01202          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_NOOP, NULL);
01203       } else if (!strcasecmp(action, "increase_listening_volume")) {
01204          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_LISTENING, NULL);
01205       } else if (!strcasecmp(action, "decrease_listening_volume")) {
01206          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_LISTENING, NULL);
01207       } else if (!strcasecmp(action, "increase_talking_volume")) {
01208          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_INCREASE_TALKING, NULL);
01209       } else if (!strcasecmp(action, "reset_listening_volume")) {
01210          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_LISTENING, NULL);
01211       } else if (!strcasecmp(action, "reset_talking_volume")) {
01212          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RESET_TALKING, NULL);
01213       } else if (!strcasecmp(action, "decrease_talking_volume")) {
01214          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DECREASE_TALKING, NULL);
01215       } else if (!strcasecmp(action, "admin_toggle_conference_lock")) {
01216          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_LOCK, NULL);
01217       } else if (!strcasecmp(action, "admin_toggle_mute_participants")) {
01218          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS, NULL);
01219       } else if (!strcasecmp(action, "participant_count")) {
01220          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PARTICIPANT_COUNT, NULL);
01221       } else if (!strcasecmp(action, "admin_kick_last")) {
01222          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_KICK_LAST, NULL);
01223       } else if (!strcasecmp(action, "leave_conference")) {
01224          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_LEAVE, NULL);
01225       } else if (!strcasecmp(action, "set_as_single_video_src")) {
01226          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_SET_SINGLE_VIDEO_SRC, NULL);
01227       } else if (!strcasecmp(action, "release_as_single_video_src")) {
01228          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC, NULL);
01229       } else if (!strncasecmp(action, "dialplan_exec(", 14)) {
01230          ast_copy_string(buf, action, sizeof(buf));
01231          action_args = buf;
01232          if ((action_args = strchr(action, '('))) {
01233             action_args++;
01234          }
01235          /* it is possible that this argument may or may not
01236           * have a closing brace at this point, it all depends on if
01237           * comma delimited arguments were provided */
01238          if ((tmp = strchr(action, ')'))) {
01239             *tmp = '\0';
01240          }
01241          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_DIALPLAN_EXEC, action_args);
01242       } else if (action_len >= 21 && !strncasecmp(action, "playback_and_continue(", 22)) {
01243          ast_copy_string(buf, action, sizeof(buf));
01244          action_args = buf;
01245          if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
01246             *tmp = '\0';
01247             action_args++;
01248          }
01249          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK_AND_CONTINUE, action_args);
01250       } else if (action_len >= 8 && !strncasecmp(action, "playback(", 9)) {
01251          ast_copy_string(buf, action, sizeof(buf));
01252          action_args = buf;
01253          if ((action_args = strchr(action, '(')) && (tmp = strrchr(action_args, ')'))) {
01254             *tmp = '\0';
01255             action_args++;
01256          }
01257          res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_PLAYBACK, action_args);
01258       }
01259    }
01260 
01261    /* if adding any of the actions failed, bail */
01262    if (res) {
01263       struct conf_menu_action *menu_action;
01264       while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
01265          ast_free(menu_action);
01266       }
01267       ast_free(menu_entry);
01268       return -1;
01269    }
01270 
01271    /* remove any list entry with an identical DTMF sequence for overrides */
01272    AST_LIST_TRAVERSE_SAFE_BEGIN(&menu->entries, cur, entry) {
01273       if (!strcasecmp(cur->dtmf, menu_entry->dtmf)) {
01274          AST_LIST_REMOVE_CURRENT(entry);
01275          ast_free(cur);
01276          break;
01277       }
01278    }
01279    AST_LIST_TRAVERSE_SAFE_END;
01280 
01281    AST_LIST_INSERT_TAIL(&menu->entries, menu_entry, entry);
01282 
01283    return 0;
01284 }
01285 
01286 static char *complete_user_profile_name(const char *line, const char *word, int pos, int state)
01287 {
01288    int which = 0;
01289    char *res = NULL;
01290    int wordlen = strlen(word);
01291    struct ao2_iterator i;
01292    struct user_profile *u_profile = NULL;
01293    RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
01294 
01295    if (!cfg) {
01296       return NULL;
01297    }
01298 
01299    i = ao2_iterator_init(cfg->user_profiles, 0);
01300    while ((u_profile = ao2_iterator_next(&i))) {
01301       if (!strncasecmp(u_profile->name, word, wordlen) && ++which > state) {
01302          res = ast_strdup(u_profile->name);
01303          ao2_ref(u_profile, -1);
01304          break;
01305       }
01306       ao2_ref(u_profile, -1);
01307    }
01308    ao2_iterator_destroy(&i);
01309 
01310    return res;
01311 }
01312 
01313 static char *handle_cli_confbridge_show_user_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01314 {
01315    struct ao2_iterator it;
01316    struct user_profile *u_profile;
01317    RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
01318 
01319    switch (cmd) {
01320    case CLI_INIT:
01321       e->command = "confbridge show profile users";
01322       e->usage =
01323          "Usage confbridge show profile users\n";
01324       return NULL;
01325    case CLI_GENERATE:
01326       return NULL;
01327    }
01328 
01329    if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
01330       return NULL;
01331    }
01332 
01333    ast_cli(a->fd,"--------- User Profiles -----------\n");
01334    ao2_lock(cfg->user_profiles);
01335    it = ao2_iterator_init(cfg->user_profiles, 0);
01336    while ((u_profile = ao2_iterator_next(&it))) {
01337       ast_cli(a->fd,"%s\n", u_profile->name);
01338       ao2_ref(u_profile, -1);
01339    }
01340    ao2_iterator_destroy(&it);
01341    ao2_unlock(cfg->user_profiles);
01342 
01343    return CLI_SUCCESS;
01344 }
01345 static char *handle_cli_confbridge_show_user_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01346 {
01347    struct user_profile u_profile;
01348 
01349    switch (cmd) {
01350    case CLI_INIT:
01351       e->command = "confbridge show profile user";
01352       e->usage =
01353          "Usage confbridge show profile user [<profile name>]\n";
01354       return NULL;
01355    case CLI_GENERATE:
01356       if (a->pos == 4) {
01357          return complete_user_profile_name(a->line, a->word, a->pos, a->n);
01358       }
01359       return NULL;
01360    }
01361 
01362    if (a->argc != 5) {
01363       return CLI_SHOWUSAGE;
01364    }
01365 
01366    if (!(conf_find_user_profile(NULL, a->argv[4], &u_profile))) {
01367       ast_cli(a->fd, "No conference user profile named '%s' found!\n", a->argv[4]);
01368       return CLI_SUCCESS;
01369    }
01370 
01371    ast_cli(a->fd,"--------------------------------------------\n");
01372    ast_cli(a->fd,"Name:                    %s\n",
01373       u_profile.name);
01374    ast_cli(a->fd,"Admin:                   %s\n",
01375       u_profile.flags & USER_OPT_ADMIN ?
01376       "true" : "false");
01377    ast_cli(a->fd,"Marked User:             %s\n",
01378       u_profile.flags & USER_OPT_MARKEDUSER ?
01379       "true" : "false");
01380    ast_cli(a->fd,"Start Muted:             %s\n",
01381       u_profile.flags & USER_OPT_STARTMUTED?
01382       "true" : "false");
01383    ast_cli(a->fd,"MOH When Empty:          %s\n",
01384       u_profile.flags & USER_OPT_MUSICONHOLD ?
01385       "enabled" : "disabled");
01386    ast_cli(a->fd,"MOH Class:               %s\n",
01387       ast_strlen_zero(u_profile.moh_class) ?
01388       "default" : u_profile.moh_class);
01389    ast_cli(a->fd,"Announcement:            %s\n",
01390       u_profile.announcement);
01391    ast_cli(a->fd,"Quiet:                   %s\n",
01392       u_profile.flags & USER_OPT_QUIET ?
01393       "enabled" : "disabled");
01394    ast_cli(a->fd,"Wait Marked:             %s\n",
01395       u_profile.flags & USER_OPT_WAITMARKED ?
01396       "enabled" : "disabled");
01397    ast_cli(a->fd,"END Marked:              %s\n",
01398       u_profile.flags & USER_OPT_ENDMARKED ?
01399       "enabled" : "disabled");
01400    ast_cli(a->fd,"Drop_silence:            %s\n",
01401       u_profile.flags & USER_OPT_DROP_SILENCE ?
01402       "enabled" : "disabled");
01403    ast_cli(a->fd,"Silence Threshold:       %ums\n",
01404       u_profile.silence_threshold);
01405    ast_cli(a->fd,"Talking Threshold:       %ums\n",
01406       u_profile.talking_threshold);
01407    ast_cli(a->fd,"Denoise:                 %s\n",
01408       u_profile.flags & USER_OPT_DENOISE ?
01409       "enabled" : "disabled");
01410    ast_cli(a->fd,"Jitterbuffer:            %s\n",
01411       u_profile.flags & USER_OPT_JITTERBUFFER ?
01412       "enabled" : "disabled");
01413    ast_cli(a->fd,"Talk Detect Events:      %s\n",
01414       u_profile.flags & USER_OPT_TALKER_DETECT ?
01415       "enabled" : "disabled");
01416    ast_cli(a->fd,"DTMF Pass Through:       %s\n",
01417       u_profile.flags & USER_OPT_DTMF_PASS ?
01418       "enabled" : "disabled");
01419    ast_cli(a->fd,"PIN:                     %s\n",
01420       ast_strlen_zero(u_profile.pin) ?
01421       "None" : u_profile.pin);
01422    ast_cli(a->fd,"Announce User Count:     %s\n",
01423       u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNT ?
01424       "enabled" : "disabled");
01425    ast_cli(a->fd,"Announce join/leave:     %s\n",
01426       u_profile.flags & (USER_OPT_ANNOUNCE_JOIN_LEAVE | USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW) ?
01427       u_profile.flags & USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW ?
01428       "enabled (with review)" : "enabled" : "disabled");
01429    ast_cli(a->fd,"Announce User Count all: %s\n",
01430       u_profile.flags & USER_OPT_ANNOUNCEUSERCOUNTALL ?
01431       "enabled" : "disabled");
01432       ast_cli(a->fd,"\n");
01433 
01434    return CLI_SUCCESS;
01435 }
01436 
01437 static char *complete_bridge_profile_name(const char *line, const char *word, int pos, int state)
01438 {
01439    int which = 0;
01440    char *res = NULL;
01441    int wordlen = strlen(word);
01442    struct ao2_iterator i;
01443    struct bridge_profile *b_profile = NULL;
01444    RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
01445 
01446    if (!cfg) {
01447       return NULL;
01448    }
01449 
01450    i = ao2_iterator_init(cfg->bridge_profiles, 0);
01451    while ((b_profile = ao2_iterator_next(&i))) {
01452       if (!strncasecmp(b_profile->name, word, wordlen) && ++which > state) {
01453          res = ast_strdup(b_profile->name);
01454          ao2_ref(b_profile, -1);
01455          break;
01456       }
01457       ao2_ref(b_profile, -1);
01458    }
01459    ao2_iterator_destroy(&i);
01460 
01461    return res;
01462 }
01463 
01464 static char *handle_cli_confbridge_show_bridge_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01465 {
01466    struct ao2_iterator it;
01467    struct bridge_profile *b_profile;
01468    RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
01469 
01470    switch (cmd) {
01471    case CLI_INIT:
01472       e->command = "confbridge show profile bridges";
01473       e->usage =
01474          "Usage confbridge show profile bridges\n";
01475       return NULL;
01476    case CLI_GENERATE:
01477       return NULL;
01478    }
01479 
01480    if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
01481       return NULL;
01482    }
01483 
01484    ast_cli(a->fd,"--------- Bridge Profiles -----------\n");
01485    ao2_lock(cfg->bridge_profiles);
01486    it = ao2_iterator_init(cfg->bridge_profiles, 0);
01487    while ((b_profile = ao2_iterator_next(&it))) {
01488       ast_cli(a->fd,"%s\n", b_profile->name);
01489       ao2_ref(b_profile, -1);
01490    }
01491    ao2_iterator_destroy(&it);
01492    ao2_unlock(cfg->bridge_profiles);
01493 
01494    return CLI_SUCCESS;
01495 }
01496 
01497 static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01498 {
01499    struct bridge_profile b_profile;
01500    char tmp[64];
01501 
01502    switch (cmd) {
01503    case CLI_INIT:
01504       e->command = "confbridge show profile bridge";
01505       e->usage =
01506          "Usage confbridge show profile bridge <profile name>\n";
01507       return NULL;
01508    case CLI_GENERATE:
01509       if (a->pos == 4) {
01510          return complete_bridge_profile_name(a->line, a->word, a->pos, a->n);
01511       }
01512       return NULL;
01513    }
01514 
01515    if (a->argc != 5) {
01516       return CLI_SHOWUSAGE;
01517    }
01518 
01519    if (!(conf_find_bridge_profile(NULL, a->argv[4], &b_profile))) {
01520       ast_cli(a->fd, "No conference bridge profile named '%s' found!\n", a->argv[4]);
01521       return CLI_SUCCESS;
01522    }
01523 
01524    ast_cli(a->fd,"--------------------------------------------\n");
01525    ast_cli(a->fd,"Name:                 %s\n", b_profile.name);
01526    ast_cli(a->fd,"Language:             %s\n", b_profile.language);
01527 
01528    if (b_profile.internal_sample_rate) {
01529       snprintf(tmp, sizeof(tmp), "%u", b_profile.internal_sample_rate);
01530    } else {
01531       ast_copy_string(tmp, "auto", sizeof(tmp));
01532    }
01533    ast_cli(a->fd,"Internal Sample Rate: %s\n", tmp);
01534 
01535    if (b_profile.mix_interval) {
01536       ast_cli(a->fd,"Mixing Interval:      %u\n", b_profile.mix_interval);
01537    } else {
01538       ast_cli(a->fd,"Mixing Interval:      Default 20ms\n");
01539    }
01540 
01541    ast_cli(a->fd,"Record Conference:    %s\n",
01542       b_profile.flags & BRIDGE_OPT_RECORD_CONFERENCE ?
01543       "yes" : "no");
01544 
01545    ast_cli(a->fd,"Record File Append:    %s\n",
01546       b_profile.flags & BRIDGE_OPT_RECORD_FILE_APPEND ?
01547       "yes" : "no");
01548 
01549    ast_cli(a->fd,"Record File Timestamp: %s\n",
01550       b_profile.flags & BRIDGE_OPT_RECORD_FILE_TIMESTAMP ?
01551       "yes" : "no");
01552 
01553    ast_cli(a->fd,"Record File:          %s\n",
01554       ast_strlen_zero(b_profile.rec_file) ? "Auto Generated" :
01555       b_profile.rec_file);
01556 
01557    ast_cli(a->fd,"Record Options:       %s\n",
01558       b_profile.rec_options);
01559 
01560    ast_cli(a->fd,"Record Command:       %s\n",
01561       b_profile.rec_command);
01562 
01563    if (b_profile.max_members) {
01564       ast_cli(a->fd,"Max Members:          %u\n", b_profile.max_members);
01565    } else {
01566       ast_cli(a->fd,"Max Members:          No Limit\n");
01567    }
01568 
01569    switch (b_profile.flags
01570       & (BRIDGE_OPT_VIDEO_SRC_LAST_MARKED | BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
01571          | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
01572    case BRIDGE_OPT_VIDEO_SRC_LAST_MARKED:
01573       ast_cli(a->fd, "Video Mode:           last_marked\n");
01574       break;
01575    case BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED:
01576       ast_cli(a->fd, "Video Mode:           first_marked\n");
01577       break;
01578    case BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER:
01579       ast_cli(a->fd, "Video Mode:           follow_talker\n");
01580       break;
01581    case 0:
01582       ast_cli(a->fd, "Video Mode:           no video\n");
01583       break;
01584    default:
01585       /* Opps.  We have more than one video mode flag set. */
01586       ast_assert(0);
01587       break;
01588    }
01589 
01590    ast_cli(a->fd,"sound_only_person:    %s\n", conf_get_sound(CONF_SOUND_ONLY_PERSON, b_profile.sounds));
01591    ast_cli(a->fd,"sound_only_one:       %s\n", conf_get_sound(CONF_SOUND_ONLY_ONE, b_profile.sounds));
01592    ast_cli(a->fd,"sound_has_joined:     %s\n", conf_get_sound(CONF_SOUND_HAS_JOINED, b_profile.sounds));
01593    ast_cli(a->fd,"sound_has_left:       %s\n", conf_get_sound(CONF_SOUND_HAS_LEFT, b_profile.sounds));
01594    ast_cli(a->fd,"sound_kicked:         %s\n", conf_get_sound(CONF_SOUND_KICKED, b_profile.sounds));
01595    ast_cli(a->fd,"sound_muted:          %s\n", conf_get_sound(CONF_SOUND_MUTED, b_profile.sounds));
01596    ast_cli(a->fd,"sound_unmuted:        %s\n", conf_get_sound(CONF_SOUND_UNMUTED, b_profile.sounds));
01597    ast_cli(a->fd,"sound_there_are:      %s\n", conf_get_sound(CONF_SOUND_THERE_ARE, b_profile.sounds));
01598    ast_cli(a->fd,"sound_other_in_party: %s\n", conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, b_profile.sounds));
01599    ast_cli(a->fd,"sound_place_into_conference: %s\n", conf_get_sound(CONF_SOUND_PLACE_IN_CONF, b_profile.sounds));
01600    ast_cli(a->fd,"sound_wait_for_leader:       %s\n", conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, b_profile.sounds));
01601    ast_cli(a->fd,"sound_leader_has_left:       %s\n", conf_get_sound(CONF_SOUND_LEADER_HAS_LEFT, b_profile.sounds));
01602    ast_cli(a->fd,"sound_get_pin:        %s\n", conf_get_sound(CONF_SOUND_GET_PIN, b_profile.sounds));
01603    ast_cli(a->fd,"sound_invalid_pin:    %s\n", conf_get_sound(CONF_SOUND_INVALID_PIN, b_profile.sounds));
01604    ast_cli(a->fd,"sound_locked:         %s\n", conf_get_sound(CONF_SOUND_LOCKED, b_profile.sounds));
01605    ast_cli(a->fd,"sound_unlocked_now:   %s\n", conf_get_sound(CONF_SOUND_UNLOCKED_NOW, b_profile.sounds));
01606    ast_cli(a->fd,"sound_lockednow:      %s\n", conf_get_sound(CONF_SOUND_LOCKED_NOW, b_profile.sounds));
01607    ast_cli(a->fd,"sound_error_menu:     %s\n", conf_get_sound(CONF_SOUND_ERROR_MENU, b_profile.sounds));
01608    ast_cli(a->fd,"sound_join:           %s\n", conf_get_sound(CONF_SOUND_JOIN, b_profile.sounds));
01609    ast_cli(a->fd,"sound_leave:          %s\n", conf_get_sound(CONF_SOUND_LEAVE, b_profile.sounds));
01610    ast_cli(a->fd,"sound_participants_muted:     %s\n", conf_get_sound(CONF_SOUND_PARTICIPANTS_MUTED, b_profile.sounds));
01611    ast_cli(a->fd,"sound_participants_unmuted:     %s\n", conf_get_sound(CONF_SOUND_PARTICIPANTS_UNMUTED, b_profile.sounds));
01612    ast_cli(a->fd,"sound_begin:          %s\n", conf_get_sound(CONF_SOUND_BEGIN, b_profile.sounds));
01613    ast_cli(a->fd,"\n");
01614 
01615    conf_bridge_profile_destroy(&b_profile);
01616    return CLI_SUCCESS;
01617 }
01618 
01619 static char *complete_menu_name(const char *line, const char *word, int pos, int state)
01620 {
01621    int which = 0;
01622    char *res = NULL;
01623    int wordlen = strlen(word);
01624    struct ao2_iterator i;
01625    struct conf_menu *menu = NULL;
01626    RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
01627 
01628    if (!cfg) {
01629       return NULL;
01630    }
01631 
01632    i = ao2_iterator_init(cfg->menus, 0);
01633    while ((menu = ao2_iterator_next(&i))) {
01634       if (!strncasecmp(menu->name, word, wordlen) && ++which > state) {
01635          res = ast_strdup(menu->name);
01636          ao2_ref(menu, -1);
01637          break;
01638       }
01639       ao2_ref(menu, -1);
01640    }
01641    ao2_iterator_destroy(&i);
01642 
01643    return res;
01644 }
01645 
01646 static char *handle_cli_confbridge_show_menus(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01647 {
01648    struct ao2_iterator it;
01649    struct conf_menu *menu;
01650    RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
01651 
01652    switch (cmd) {
01653    case CLI_INIT:
01654       e->command = "confbridge show menus";
01655       e->usage =
01656          "Usage confbridge show profile menus\n";
01657       return NULL;
01658    case CLI_GENERATE:
01659       return NULL;
01660    }
01661 
01662    if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
01663       return NULL;
01664    }
01665 
01666    ast_cli(a->fd,"--------- Menus -----------\n");
01667    ao2_lock(cfg->menus);
01668    it = ao2_iterator_init(cfg->menus, 0);
01669    while ((menu = ao2_iterator_next(&it))) {
01670       ast_cli(a->fd,"%s\n", menu->name);
01671       ao2_ref(menu, -1);
01672    }
01673    ao2_iterator_destroy(&it);
01674    ao2_unlock(cfg->menus);
01675 
01676    return CLI_SUCCESS;
01677 }
01678 
01679 static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01680 {
01681    RAII_VAR(struct conf_menu *, menu, NULL, ao2_cleanup);
01682    RAII_VAR(struct confbridge_cfg *, cfg, NULL, ao2_cleanup);
01683    struct conf_menu_entry *menu_entry = NULL;
01684    struct conf_menu_action *menu_action = NULL;
01685 
01686    switch (cmd) {
01687    case CLI_INIT:
01688       e->command = "confbridge show menu";
01689       e->usage =
01690          "Usage confbridge show menu [<menu name>]\n";
01691       return NULL;
01692    case CLI_GENERATE:
01693       if (a->pos == 3) {
01694          return complete_menu_name(a->line, a->word, a->pos, a->n);
01695       }
01696       return NULL;
01697    }
01698 
01699    if (a->argc != 4) {
01700       return CLI_SHOWUSAGE;
01701    }
01702 
01703    if (!(cfg = ao2_global_obj_ref(cfg_handle))) {
01704       return NULL;
01705    }
01706 
01707    if (!(menu = menu_find(cfg->menus, a->argv[3]))) {
01708       ast_cli(a->fd, "No conference menu named '%s' found!\n", a->argv[3]);
01709       return CLI_SUCCESS;
01710    }
01711    ao2_lock(menu);
01712 
01713    ast_cli(a->fd,"Name: %s\n", menu->name);
01714    AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
01715       int action_num = 0;
01716       ast_cli(a->fd, "%s=", menu_entry->dtmf);
01717       AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
01718          if (action_num) {
01719             ast_cli(a->fd, ", ");
01720          }
01721          switch (menu_action->id) {
01722          case MENU_ACTION_TOGGLE_MUTE:
01723             ast_cli(a->fd, "toggle_mute");
01724             break;
01725          case MENU_ACTION_NOOP:
01726             ast_cli(a->fd, "no_op");
01727             break;
01728          case MENU_ACTION_INCREASE_LISTENING:
01729             ast_cli(a->fd, "increase_listening_volume");
01730             break;
01731          case MENU_ACTION_DECREASE_LISTENING:
01732             ast_cli(a->fd, "decrease_listening_volume");
01733             break;
01734          case MENU_ACTION_RESET_LISTENING:
01735             ast_cli(a->fd, "reset_listening_volume");
01736             break;
01737          case MENU_ACTION_RESET_TALKING:
01738             ast_cli(a->fd, "reset_talking_volume");
01739             break;
01740          case MENU_ACTION_INCREASE_TALKING:
01741             ast_cli(a->fd, "increase_talking_volume");
01742             break;
01743          case MENU_ACTION_DECREASE_TALKING:
01744             ast_cli(a->fd, "decrease_talking_volume");
01745             break;
01746          case MENU_ACTION_PLAYBACK:
01747             ast_cli(a->fd, "playback(%s)", menu_action->data.playback_file);
01748             break;
01749          case MENU_ACTION_PLAYBACK_AND_CONTINUE:
01750             ast_cli(a->fd, "playback_and_continue(%s)", menu_action->data.playback_file);
01751             break;
01752          case MENU_ACTION_DIALPLAN_EXEC:
01753             ast_cli(a->fd, "dialplan_exec(%s,%s,%d)",
01754                menu_action->data.dialplan_args.context,
01755                menu_action->data.dialplan_args.exten,
01756                menu_action->data.dialplan_args.priority);
01757             break;
01758          case MENU_ACTION_ADMIN_TOGGLE_LOCK:
01759             ast_cli(a->fd, "admin_toggle_conference_lock");
01760             break;
01761          case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
01762             ast_cli(a->fd, "admin_toggle_mute_participants");
01763             break;
01764          case MENU_ACTION_PARTICIPANT_COUNT:
01765             ast_cli(a->fd, "participant_count");
01766             break;
01767          case MENU_ACTION_ADMIN_KICK_LAST:
01768             ast_cli(a->fd, "admin_kick_last");
01769             break;
01770          case MENU_ACTION_LEAVE:
01771             ast_cli(a->fd, "leave_conference");
01772             break;
01773          case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
01774             ast_cli(a->fd, "set_as_single_video_src");
01775             break;
01776          case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
01777             ast_cli(a->fd, "release_as_single_video_src");
01778             break;
01779          }
01780          action_num++;
01781       }
01782       ast_cli(a->fd,"\n");
01783    }
01784 
01785 
01786    ao2_unlock(menu);
01787    return CLI_SUCCESS;
01788 }
01789 
01790 static struct ast_cli_entry cli_confbridge_parser[] = {
01791    AST_CLI_DEFINE(handle_cli_confbridge_show_user_profile, "Show a conference user profile."),
01792    AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profile, "Show a conference bridge profile."),
01793    AST_CLI_DEFINE(handle_cli_confbridge_show_menu, "Show a conference menu"),
01794    AST_CLI_DEFINE(handle_cli_confbridge_show_user_profiles, "Show a list of conference user profiles."),
01795    AST_CLI_DEFINE(handle_cli_confbridge_show_bridge_profiles, "Show a list of conference bridge profiles."),
01796    AST_CLI_DEFINE(handle_cli_confbridge_show_menus, "Show a list of conference menus"),
01797 
01798 };
01799 
01800 static void confbridge_cfg_destructor(void *obj)
01801 {
01802    struct confbridge_cfg *cfg = obj;
01803    ao2_cleanup(cfg->user_profiles);
01804    ao2_cleanup(cfg->bridge_profiles);
01805    ao2_cleanup(cfg->menus);
01806 }
01807 
01808 void *confbridge_cfg_alloc(void)
01809 {
01810    struct confbridge_cfg *cfg;
01811 
01812    if (!(cfg = ao2_alloc(sizeof(*cfg), confbridge_cfg_destructor))) {
01813       return NULL;
01814    }
01815 
01816    if (!(cfg->user_profiles = ao2_container_alloc(283, user_hash_cb, user_cmp_cb))) {
01817       goto error;
01818    }
01819 
01820    if (!(cfg->bridge_profiles = ao2_container_alloc(283, bridge_hash_cb, bridge_cmp_cb))) {
01821       goto error;
01822    }
01823 
01824    if (!(cfg->menus = ao2_container_alloc(283, menu_hash_cb, menu_cmp_cb))) {
01825       goto error;
01826    }
01827 
01828    return cfg;
01829 error:
01830    ao2_ref(cfg, -1);
01831    return NULL;
01832 }
01833 
01834 static int announce_user_count_all_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
01835 {
01836    struct user_profile *u_profile = obj;
01837 
01838    if (strcasecmp(var->name, "announce_user_count_all")) {
01839       return -1;
01840    }
01841    if (ast_true(var->value)) {
01842       u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL;
01843    } else if (ast_false(var->value)) {
01844       u_profile->flags = u_profile->flags & ~USER_OPT_ANNOUNCEUSERCOUNTALL;
01845    } else if (sscanf(var->value, "%30u", &u_profile->announce_user_count_all_after) == 1) {
01846       u_profile->flags = u_profile->flags | USER_OPT_ANNOUNCEUSERCOUNTALL;
01847    } else {
01848       return -1;
01849    }
01850    return 0;
01851 }
01852 
01853 static int mix_interval_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
01854 {
01855    struct bridge_profile *b_profile = obj;
01856 
01857    if (strcasecmp(var->name, "mixing_interval")) {
01858       return -1;
01859    }
01860    if (sscanf(var->value, "%30u", &b_profile->mix_interval) != 1) {
01861       return -1;
01862    }
01863    switch (b_profile->mix_interval) {
01864    case 10:
01865    case 20:
01866    case 40:
01867    case 80:
01868       return 0;
01869    default:
01870       return -1;
01871    }
01872 }
01873 
01874 static int video_mode_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
01875 {
01876    struct bridge_profile *b_profile = obj;
01877 
01878    if (strcasecmp(var->name, "video_mode")) {
01879       return -1;
01880    }
01881    if (!strcasecmp(var->value, "first_marked")) {
01882       ast_set_flags_to(b_profile,
01883          BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
01884             | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
01885             | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER,
01886          BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED);
01887    } else if (!strcasecmp(var->value, "last_marked")) {
01888       ast_set_flags_to(b_profile,
01889          BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
01890             | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
01891             | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER,
01892          BRIDGE_OPT_VIDEO_SRC_LAST_MARKED);
01893    } else if (!strcasecmp(var->value, "follow_talker")) {
01894       ast_set_flags_to(b_profile,
01895          BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
01896             | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
01897             | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER,
01898          BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
01899    } else if (!strcasecmp(var->value, "none")) {
01900       ast_clear_flag(b_profile,
01901          BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
01902             | BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
01903             | BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
01904    } else {
01905       return -1;
01906    }
01907    return 0;
01908 }
01909 
01910 static int user_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
01911 {
01912    struct user_profile *u_profile = obj;
01913 
01914    return conf_find_user_profile(NULL, var->value, u_profile) ? 0 : -1;
01915 }
01916 
01917 static int bridge_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
01918 {
01919    struct bridge_profile *b_profile = obj;
01920    struct bridge_profile_sounds *sounds = bridge_profile_sounds_alloc();
01921    struct bridge_profile_sounds *oldsounds = b_profile->sounds;
01922 
01923    if (!sounds) {
01924       return -1;
01925    }
01926    if (!(conf_find_bridge_profile(NULL, var->value, b_profile))) {
01927       ao2_ref(sounds, -1);
01928       return -1;
01929    }
01930    /* Using a bridge profile as a template is a little complicated due to the sounds. Since the sounds
01931     * structure of a dynamic profile will need to be altered, a completely new sounds structure must be
01932     * created instead of simply holding a reference to the one built by the config file. */
01933    ast_string_field_set(sounds, onlyperson, b_profile->sounds->onlyperson);
01934    ast_string_field_set(sounds, onlyone, b_profile->sounds->onlyone);
01935    ast_string_field_set(sounds, hasjoin, b_profile->sounds->hasjoin);
01936    ast_string_field_set(sounds, hasleft, b_profile->sounds->hasleft);
01937    ast_string_field_set(sounds, kicked, b_profile->sounds->kicked);
01938    ast_string_field_set(sounds, muted, b_profile->sounds->muted);
01939    ast_string_field_set(sounds, unmuted, b_profile->sounds->unmuted);
01940    ast_string_field_set(sounds, thereare, b_profile->sounds->thereare);
01941    ast_string_field_set(sounds, otherinparty, b_profile->sounds->otherinparty);
01942    ast_string_field_set(sounds, placeintoconf, b_profile->sounds->placeintoconf);
01943    ast_string_field_set(sounds, waitforleader, b_profile->sounds->waitforleader);
01944    ast_string_field_set(sounds, leaderhasleft, b_profile->sounds->leaderhasleft);
01945    ast_string_field_set(sounds, getpin, b_profile->sounds->getpin);
01946    ast_string_field_set(sounds, invalidpin, b_profile->sounds->invalidpin);
01947    ast_string_field_set(sounds, locked, b_profile->sounds->locked);
01948    ast_string_field_set(sounds, unlockednow, b_profile->sounds->unlockednow);
01949    ast_string_field_set(sounds, lockednow, b_profile->sounds->lockednow);
01950    ast_string_field_set(sounds, errormenu, b_profile->sounds->errormenu);
01951    ast_string_field_set(sounds, join, b_profile->sounds->join);
01952    ast_string_field_set(sounds, leave, b_profile->sounds->leave);
01953    ast_string_field_set(sounds, participantsmuted, b_profile->sounds->participantsmuted);
01954    ast_string_field_set(sounds, participantsunmuted, b_profile->sounds->participantsunmuted);
01955    ast_string_field_set(sounds, begin, b_profile->sounds->begin);
01956 
01957    ao2_ref(b_profile->sounds, -1); /* sounds struct copied over to it from the template by reference only. */
01958    ao2_ref(oldsounds, -1);    /* original sounds struct we don't need anymore */
01959    b_profile->sounds = sounds;     /* the new sounds struct that is a deep copy of the one from the template. */
01960 
01961    return 0;
01962 }
01963 
01964 static int copy_menu_entry(struct conf_menu_entry *dst, struct conf_menu_entry *src)
01965 {
01966    struct conf_menu_action *menu_action;
01967    struct conf_menu_action *new_menu_action;
01968 
01969    ast_copy_string(dst->dtmf, src->dtmf, sizeof(dst->dtmf));
01970    AST_LIST_HEAD_INIT_NOLOCK(&dst->actions);
01971 
01972    AST_LIST_TRAVERSE(&src->actions, menu_action, action) {
01973       if (!(new_menu_action = ast_calloc(1, sizeof(*new_menu_action)))) {
01974          return -1;
01975       }
01976       memcpy(new_menu_action, menu_action, sizeof(*new_menu_action));
01977       AST_LIST_NEXT(new_menu_action, action) = NULL;
01978       AST_LIST_INSERT_TAIL(&dst->actions, new_menu_action, action);
01979    }
01980 
01981    return 0;
01982 }
01983 
01984 static int conf_menu_profile_copy(struct conf_menu *dst, struct conf_menu *src)
01985 {
01986    /* Copy each menu item to the dst struct */
01987    struct conf_menu_entry *cur;
01988 
01989    AST_LIST_TRAVERSE(&src->entries, cur, entry) {
01990       struct conf_menu_entry *cpy;
01991 
01992       if (!(cpy = ast_calloc(1, sizeof(*cpy)))) {
01993          return -1;
01994       }
01995 
01996       if (copy_menu_entry(cpy, cur)) {
01997          conf_menu_entry_destroy(cpy);
01998          ast_free(cpy);
01999          return -1;
02000       }
02001       AST_LIST_INSERT_TAIL(&dst->entries, cpy, entry);
02002    }
02003 
02004    return 0;
02005 }
02006 
02007 static int menu_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
02008 {
02009    struct conf_menu *dst_menu = obj;
02010    struct confbridge_cfg *cfg = aco_pending_config(&cfg_info);
02011    RAII_VAR(struct conf_menu *, src_menu, NULL, ao2_cleanup);
02012 
02013    if (!cfg) {
02014       return 0;
02015    }
02016 
02017    if (!(src_menu = ao2_find(cfg->menus, var->value, OBJ_KEY))) {
02018       return -1;
02019    }
02020 
02021    if (conf_menu_profile_copy(dst_menu, src_menu)) {
02022       return -1;
02023    }
02024 
02025    return 0;
02026 }
02027 
02028 static int sound_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
02029 {
02030    set_sound(var->name, var->value, obj);
02031    return 0;
02032 }
02033 
02034 static int menu_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
02035 {
02036    add_menu_entry(obj, var->name, var->value);
02037    return 0;
02038 }
02039 
02040 static int verify_default_profiles(void)
02041 {
02042    RAII_VAR(struct user_profile *, user_profile, NULL, ao2_cleanup);
02043    RAII_VAR(struct bridge_profile *, bridge_profile, NULL, ao2_cleanup);
02044    RAII_VAR(struct conf_menu *, menu_profile, NULL, ao2_cleanup);
02045    struct confbridge_cfg *cfg = aco_pending_config(&cfg_info);
02046 
02047    if (!cfg) {
02048       return 0;
02049    }
02050 
02051    bridge_profile = ao2_find(cfg->bridge_profiles, DEFAULT_BRIDGE_PROFILE, OBJ_KEY);
02052    if (!bridge_profile) {
02053       bridge_profile = bridge_profile_alloc(DEFAULT_BRIDGE_PROFILE);
02054       if (!bridge_profile) {
02055          return -1;
02056       }
02057       ast_log(AST_LOG_NOTICE, "Adding %s profile to app_confbridge\n", DEFAULT_BRIDGE_PROFILE);
02058       aco_set_defaults(&bridge_type, DEFAULT_BRIDGE_PROFILE, bridge_profile);
02059       ao2_link(cfg->bridge_profiles, bridge_profile);
02060    }
02061 
02062    user_profile = ao2_find(cfg->user_profiles, DEFAULT_USER_PROFILE, OBJ_KEY);
02063    if (!user_profile) {
02064       user_profile = user_profile_alloc(DEFAULT_USER_PROFILE);
02065       if (!user_profile) {
02066          return -1;
02067       }
02068       ast_log(AST_LOG_NOTICE, "Adding %s profile to app_confbridge\n", DEFAULT_USER_PROFILE);
02069       aco_set_defaults(&user_type, DEFAULT_USER_PROFILE, user_profile);
02070       ao2_link(cfg->user_profiles, user_profile);
02071    }
02072 
02073    menu_profile = ao2_find(cfg->menus, DEFAULT_MENU_PROFILE, OBJ_KEY);
02074    if (!menu_profile) {
02075       menu_profile = menu_alloc(DEFAULT_MENU_PROFILE);
02076       if (!menu_profile) {
02077          return -1;
02078       }
02079       ast_log(AST_LOG_NOTICE, "Adding %s menu to app_confbridge\n", DEFAULT_MENU_PROFILE);
02080       aco_set_defaults(&menu_type, DEFAULT_MENU_PROFILE, menu_profile);
02081       ao2_link(cfg->menus, menu_profile);
02082    }
02083 
02084    return 0;
02085 }
02086 
02087 int conf_load_config(void)
02088 {
02089    if (aco_info_init(&cfg_info)) {
02090       return -1;
02091    }
02092 
02093    /* User options */
02094    aco_option_register(&cfg_info, "type", ACO_EXACT, user_types, NULL, OPT_NOOP_T, 0, 0);
02095    aco_option_register(&cfg_info, "admin", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ADMIN);
02096    aco_option_register(&cfg_info, "marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_MARKEDUSER);
02097    aco_option_register(&cfg_info, "startmuted", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_STARTMUTED);
02098    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);
02099    aco_option_register(&cfg_info, "quiet", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_QUIET);
02100    aco_option_register_custom(&cfg_info, "announce_user_count_all", ACO_EXACT, user_types, "no", announce_user_count_all_handler, 0);
02101    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);
02102    /* Negative logic. Defaults to "yes" and evaluates with ast_false(). If !ast_false(), USER_OPT_NOONLYPERSON is cleared */
02103    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);
02104    aco_option_register(&cfg_info, "wait_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_WAITMARKED);
02105    aco_option_register(&cfg_info, "end_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKED);
02106    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);
02107    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);
02108    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);
02109    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);
02110    aco_option_register(&cfg_info, "pin", ACO_EXACT, user_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct user_profile, pin));
02111    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));
02112    aco_option_register(&cfg_info, "announcement", ACO_EXACT, user_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct user_profile, announcement));
02113    aco_option_register(&cfg_info, "denoise", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_DENOISE);
02114    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);
02115    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));
02116    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));
02117    aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_JITTERBUFFER);
02118    /* This option should only be used with the CONFBRIDGE dialplan function */
02119    aco_option_register_custom(&cfg_info, "template", ACO_EXACT, user_types, NULL, user_template_handler, 0);
02120 
02121 /* XXX ASTERISK-21271 need a user supplied bridge merge_priority to merge ConfBridges (default = 1, range 1-INT_MAX) */
02122    /* Bridge options */
02123    aco_option_register(&cfg_info, "type", ACO_EXACT, bridge_types, NULL, OPT_NOOP_T, 0, 0);
02124    aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), USER_OPT_JITTERBUFFER);
02125    /* "auto" will fail to parse as a uint, but we use PARSE_DEFAULT to set the value to 0 in that case, which is the value that auto resolves to */
02126    aco_option_register(&cfg_info, "internal_sample_rate", ACO_EXACT, bridge_types, "0", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct bridge_profile, internal_sample_rate), 0);
02127    aco_option_register_custom(&cfg_info, "mixing_interval", ACO_EXACT, bridge_types, "20", mix_interval_handler, 0);
02128    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);
02129    aco_option_register_custom(&cfg_info, "video_mode", ACO_EXACT, bridge_types, NULL, video_mode_handler, 0);
02130    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);
02131    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);
02132    aco_option_register(&cfg_info, "max_members", ACO_EXACT, bridge_types, "0", OPT_UINT_T, 0, FLDSET(struct bridge_profile, max_members));
02133    aco_option_register(&cfg_info, "record_file", ACO_EXACT, bridge_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, rec_file));
02134    aco_option_register(&cfg_info, "record_options", ACO_EXACT, bridge_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, rec_options));
02135    aco_option_register(&cfg_info, "record_command", ACO_EXACT, bridge_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, rec_command));
02136    aco_option_register(&cfg_info, "language", ACO_EXACT, bridge_types, "en", OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, language));
02137    aco_option_register_custom(&cfg_info, "^sound_", ACO_REGEX, bridge_types, NULL, sound_option_handler, 0);
02138    /* This option should only be used with the CONFBRIDGE dialplan function */
02139    aco_option_register_custom(&cfg_info, "template", ACO_EXACT, bridge_types, NULL, bridge_template_handler, 0);
02140 
02141    /* Menu options */
02142    aco_option_register(&cfg_info, "type", ACO_EXACT, menu_types, NULL, OPT_NOOP_T, 0, 0);
02143    aco_option_register_custom(&cfg_info, "template", ACO_EXACT, menu_types, NULL, menu_template_handler, 0);
02144    aco_option_register_custom(&cfg_info, "^[0-9A-D*#]+$", ACO_REGEX, menu_types, NULL, menu_option_handler, 0);
02145 
02146    if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
02147       goto error;
02148    }
02149 
02150    if (ast_cli_register_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser))) {
02151       goto error;
02152    }
02153 
02154    return 0;
02155 error:
02156    conf_destroy_config();
02157    return -1;
02158 }
02159 
02160 int conf_reload_config(void)
02161 {
02162    if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
02163       /* On a reload, just keep the config we already have in place. */
02164       return -1;
02165    }
02166    return 0;
02167 }
02168 
02169 static void conf_user_profile_copy(struct user_profile *dst, struct user_profile *src)
02170 {
02171    *dst = *src;
02172 }
02173 
02174 const struct user_profile *conf_find_user_profile(struct ast_channel *chan, const char *user_profile_name, struct user_profile *result)
02175 {
02176    struct user_profile *tmp2;
02177    struct ast_datastore *datastore = NULL;
02178    struct func_confbridge_data *b_data = NULL;
02179    RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
02180 
02181    if (chan && ast_strlen_zero(user_profile_name)) {
02182       ast_channel_lock(chan);
02183       datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
02184       ast_channel_unlock(chan);
02185       if (datastore) {
02186          b_data = datastore->data;
02187          if (b_data->u_usable) {
02188             conf_user_profile_copy(result, &b_data->u_profile);
02189             return result;
02190          }
02191       }
02192    }
02193 
02194    if (!cfg) {
02195       return NULL;
02196    }
02197    if (ast_strlen_zero(user_profile_name)) {
02198       user_profile_name = DEFAULT_USER_PROFILE;
02199    }
02200    if (!(tmp2 = ao2_find(cfg->user_profiles, user_profile_name, OBJ_KEY))) {
02201       return NULL;
02202    }
02203    ao2_lock(tmp2);
02204    conf_user_profile_copy(result, tmp2);
02205    ao2_unlock(tmp2);
02206    ao2_ref(tmp2, -1);
02207 
02208    return result;
02209 }
02210 
02211 void conf_bridge_profile_copy(struct bridge_profile *dst, struct bridge_profile *src)
02212 {
02213    *dst = *src;
02214    if (src->sounds) {
02215       ao2_ref(src->sounds, +1);
02216    }
02217 }
02218 
02219 void conf_bridge_profile_destroy(struct bridge_profile *b_profile)
02220 {
02221    if (b_profile->sounds) {
02222       ao2_ref(b_profile->sounds, -1);
02223       b_profile->sounds = NULL;
02224    }
02225 }
02226 
02227 const struct bridge_profile *conf_find_bridge_profile(struct ast_channel *chan, const char *bridge_profile_name, struct bridge_profile *result)
02228 {
02229    struct bridge_profile *tmp2;
02230    struct ast_datastore *datastore = NULL;
02231    struct func_confbridge_data *b_data = NULL;
02232    RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
02233 
02234    if (chan && ast_strlen_zero(bridge_profile_name)) {
02235       ast_channel_lock(chan);
02236       datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
02237       ast_channel_unlock(chan);
02238       if (datastore) {
02239          b_data = datastore->data;
02240          if (b_data->b_usable) {
02241             conf_bridge_profile_copy(result, &b_data->b_profile);
02242             return result;
02243          }
02244       }
02245    }
02246 
02247    if (!cfg) {
02248       return NULL;
02249    }
02250    if (ast_strlen_zero(bridge_profile_name)) {
02251       bridge_profile_name = DEFAULT_BRIDGE_PROFILE;
02252    }
02253    if (!(tmp2 = ao2_find(cfg->bridge_profiles, bridge_profile_name, OBJ_KEY))) {
02254       return NULL;
02255    }
02256    ao2_lock(tmp2);
02257    conf_bridge_profile_copy(result, tmp2);
02258    ao2_unlock(tmp2);
02259    ao2_ref(tmp2, -1);
02260 
02261    return result;
02262 }
02263 
02264 struct dtmf_menu_hook_pvt {
02265    struct confbridge_user *user;
02266    struct conf_menu_entry menu_entry;
02267    struct conf_menu *menu;
02268 };
02269 
02270 static void menu_hook_destroy(void *hook_pvt)
02271 {
02272    struct dtmf_menu_hook_pvt *pvt = hook_pvt;
02273    struct conf_menu_action *action = NULL;
02274 
02275    ao2_cleanup(pvt->menu);
02276 
02277    while ((action = AST_LIST_REMOVE_HEAD(&pvt->menu_entry.actions, action))) {
02278       ast_free(action);
02279    }
02280    ast_free(pvt);
02281 }
02282 
02283 static int menu_hook_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
02284 {
02285    struct dtmf_menu_hook_pvt *pvt = hook_pvt;
02286 
02287    return conf_handle_dtmf(bridge_channel, pvt->user, &pvt->menu_entry, pvt->menu);
02288 }
02289 
02290 void conf_menu_entry_destroy(struct conf_menu_entry *menu_entry)
02291 {
02292    struct conf_menu_action *menu_action = NULL;
02293    while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
02294       ast_free(menu_action);
02295    }
02296 }
02297 
02298 int conf_find_menu_entry_by_sequence(const char *dtmf_sequence, struct conf_menu *menu, struct conf_menu_entry *result)
02299 {
02300    struct conf_menu_entry *menu_entry = NULL;
02301 
02302    ao2_lock(menu);
02303    AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
02304       if (!strcasecmp(menu_entry->dtmf, dtmf_sequence)) {
02305          copy_menu_entry(result, menu_entry);
02306          ao2_unlock(menu);
02307          return 1;
02308       }
02309    }
02310    ao2_unlock(menu);
02311 
02312    return 0;
02313 }
02314 
02315 static int apply_menu_to_user(struct confbridge_user *user, struct conf_menu *menu)
02316 {
02317    struct conf_menu_entry *menu_entry;
02318 
02319    SCOPED_AO2LOCK(menu_lock, menu);
02320    AST_LIST_TRAVERSE(&menu->entries, menu_entry, entry) {
02321       struct dtmf_menu_hook_pvt *pvt;
02322 
02323       if (!(pvt = ast_calloc(1, sizeof(*pvt)))) {
02324          return -1;
02325       }
02326       pvt->user = user;
02327       pvt->menu = ao2_bump(menu);
02328 
02329       if (copy_menu_entry(&pvt->menu_entry, menu_entry)) {
02330          menu_hook_destroy(pvt);
02331          return -1;
02332       }
02333 
02334       if (ast_bridge_dtmf_hook(&user->features, pvt->menu_entry.dtmf,
02335          menu_hook_callback, pvt, menu_hook_destroy, 0)) {
02336          menu_hook_destroy(pvt);
02337       }
02338    }
02339    strcpy(user->menu_name, menu->name); /* Safe */
02340 
02341    return 0;
02342 }
02343 
02344 int conf_set_menu_to_user(struct ast_channel *chan, struct confbridge_user *user, const char *menu_profile_name)
02345 {
02346    RAII_VAR(struct confbridge_cfg *, cfg, ao2_global_obj_ref(cfg_handle), ao2_cleanup);
02347    RAII_VAR(struct conf_menu *, menu, NULL, ao2_cleanup);
02348 
02349    if (chan && ast_strlen_zero(menu_profile_name)) {
02350       struct ast_datastore *datastore;
02351       struct func_confbridge_data *b_data;
02352 
02353       ast_channel_lock(chan);
02354       datastore = ast_channel_datastore_find(chan, &confbridge_datastore, NULL);
02355       ast_channel_unlock(chan);
02356       if (datastore) {
02357          /* If a menu exists in the CONFBRIDGE function datastore, use it. */
02358          b_data = datastore->data;
02359          if (b_data->m_usable) {
02360             menu = ao2_bump(b_data->menu);
02361             return apply_menu_to_user(user, menu);
02362          }
02363       }
02364    }
02365 
02366    /* Otherwise, we need to get whatever menu profile is specified to use (or default). */
02367    if (!cfg) {
02368       return -1;
02369    }
02370 
02371    if (ast_strlen_zero(menu_profile_name)) {
02372       menu_profile_name = DEFAULT_MENU_PROFILE;
02373    }
02374 
02375    if (!(menu = ao2_find(cfg->menus, menu_profile_name, OBJ_KEY))) {
02376       return -1;
02377    }
02378 
02379    return apply_menu_to_user(user, menu);
02380 }
02381 
02382 void conf_destroy_config(void)
02383 {
02384    ast_cli_unregister_multiple(cli_confbridge_parser, ARRAY_LEN(cli_confbridge_parser));
02385    aco_info_destroy(&cfg_info);
02386    ao2_global_obj_release(cfg_handle);
02387 }

Generated on Thu Apr 16 06:27:31 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6