app_meetme.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2007, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * SLA Implementation by:
00009  * Russell Bryant <russell@digium.com>
00010  *
00011  * See http://www.asterisk.org for more information about
00012  * the Asterisk project. Please do not directly contact
00013  * any of the maintainers of this project for assistance;
00014  * the project provides a web site, mailing lists and IRC
00015  * channels for your use.
00016  *
00017  * This program is free software, distributed under the terms of
00018  * the GNU General Public License Version 2. See the LICENSE file
00019  * at the top of the source tree.
00020  */
00021 
00022 /*! \file
00023  *
00024  * \brief Meet me conference bridge and Shared Line Appearances
00025  *
00026  * \author Mark Spencer <markster@digium.com>
00027  * \author (SLA) Russell Bryant <russell@digium.com>
00028  * 
00029  * \ingroup applications
00030  */
00031 
00032 /*! \li \ref app_meetme.c uses configuration file \ref meetme.conf
00033  * \addtogroup configuration_file Configuration Files
00034  */
00035 
00036 /*! 
00037  * \page meetme.conf meetme.conf
00038  * \verbinclude meetme.conf.sample
00039  */
00040 
00041 /*** MODULEINFO
00042    <depend>dahdi</depend>
00043    <defaultenabled>no</defaultenabled>
00044    <support_level>extended</support_level>
00045    <replacement>app_confbridge</replacement>
00046  ***/
00047 
00048 #include "asterisk.h"
00049 
00050 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 430509 $")
00051 
00052 #include <dahdi/user.h>
00053 
00054 #include "asterisk/lock.h"
00055 #include "asterisk/file.h"
00056 #include "asterisk/channel.h"
00057 #include "asterisk/pbx.h"
00058 #include "asterisk/module.h"
00059 #include "asterisk/config.h"
00060 #include "asterisk/app.h"
00061 #include "asterisk/dsp.h"
00062 #include "asterisk/musiconhold.h"
00063 #include "asterisk/manager.h"
00064 #include "asterisk/cli.h"
00065 #include "asterisk/say.h"
00066 #include "asterisk/utils.h"
00067 #include "asterisk/translate.h"
00068 #include "asterisk/ulaw.h"
00069 #include "asterisk/astobj2.h"
00070 #include "asterisk/devicestate.h"
00071 #include "asterisk/dial.h"
00072 #include "asterisk/causes.h"
00073 #include "asterisk/paths.h"
00074 #include "asterisk/data.h"
00075 #include "asterisk/test.h"
00076 #include "asterisk/stasis.h"
00077 #include "asterisk/stasis_channels.h"
00078 #include "asterisk/stasis_message_router.h"
00079 #include "asterisk/json.h"
00080 #include "asterisk/format_compatibility.h"
00081 
00082 #include "enter.h"
00083 #include "leave.h"
00084 
00085 /*** DOCUMENTATION
00086    <application name="MeetMe" language="en_US">
00087       <synopsis>
00088          MeetMe conference bridge.
00089       </synopsis>
00090       <syntax>
00091          <parameter name="confno">
00092             <para>The conference number</para>
00093          </parameter>
00094          <parameter name="options">
00095             <optionlist>
00096                <option name="a">
00097                   <para>Set admin mode.</para>
00098                </option>
00099                <option name="A">
00100                   <para>Set marked mode.</para>
00101                </option>
00102                <option name="b">
00103                   <para>Run AGI script specified in <variable>MEETME_AGI_BACKGROUND</variable>
00104                   Default: <literal>conf-background.agi</literal>.</para>
00105                   <note><para>This does not work with non-DAHDI channels in the same
00106                   conference).</para></note>
00107                </option>
00108                <option name="c">
00109                   <para>Announce user(s) count on joining a conference.</para>
00110                </option>
00111                <option name="C">
00112                   <para>Continue in dialplan when kicked out of conference.</para>
00113                </option>
00114                <option name="d">
00115                   <para>Dynamically add conference.</para>
00116                </option>
00117                <option name="D">
00118                   <para>Dynamically add conference, prompting for a PIN.</para>
00119                </option>
00120                <option name="e">
00121                   <para>Select an empty conference.</para>
00122                </option>
00123                <option name="E">
00124                   <para>Select an empty pinless conference.</para>
00125                </option>
00126                <option name="F">
00127                   <para>Pass DTMF through the conference.</para>
00128                </option>
00129                <option name="G">
00130                   <argument name="x" required="true">
00131                      <para>The file to playback</para>
00132                   </argument>
00133                   <para>Play an intro announcement in conference.</para>
00134                </option>
00135                <option name="i">
00136                   <para>Announce user join/leave with review.</para>
00137                </option>
00138                <option name="I">
00139                   <para>Announce user join/leave without review.</para>
00140                </option>
00141                <option name="k">
00142                   <para>Close the conference if there's only one active participant left at exit.</para>
00143                </option>
00144                <option name="l">
00145                   <para>Set listen only mode (Listen only, no talking).</para>
00146                </option>
00147                <option name="m">
00148                   <para>Set initially muted.</para>
00149                </option>
00150                <option name="M" hasparams="optional">
00151                   <para>Enable music on hold when the conference has a single caller. Optionally,
00152                   specify a musiconhold class to use. If one is not provided, it will use the
00153                   channel's currently set music class, or <literal>default</literal>.</para>
00154                   <argument name="class" required="true" />
00155                </option>
00156                <option name="n">
00157                   <para>Disable the denoiser. By default, if <literal>func_speex</literal> is loaded, Asterisk
00158                   will apply a denoiser to channels in the MeetMe conference. However, channel
00159                   drivers that present audio with a varying rate will experience degraded
00160                   performance with a denoiser attached. This parameter allows a channel joining
00161                   the conference to choose not to have a denoiser attached without having to
00162                   unload <literal>func_speex</literal>.</para>
00163                </option>
00164                <option name="o">
00165                   <para>Set talker optimization - treats talkers who aren't speaking as
00166                   being muted, meaning (a) No encode is done on transmission and (b)
00167                   Received audio that is not registered as talking is omitted causing no
00168                   buildup in background noise.</para>
00169                </option>
00170                <option name="p" hasparams="optional">
00171                   <para>Allow user to exit the conference by pressing <literal>#</literal> (default)
00172                   or any of the defined keys. Dial plan execution will continue at the next
00173                   priority following MeetMe. The key used is set to channel variable
00174                   <variable>MEETME_EXIT_KEY</variable>.</para>
00175                   <argument name="keys" required="true" />
00176                   <note>
00177                      <para>Option <literal>s</literal> has priority for <literal>*</literal>
00178                      since it cannot change its activation code.</para>
00179                   </note>
00180                </option>
00181                <option name="P">
00182                   <para>Always prompt for the pin even if it is specified.</para>
00183                </option>
00184                <option name="q">
00185                   <para>Quiet mode (don't play enter/leave sounds).</para>
00186                </option>
00187                <option name="r">
00188                   <para>Record conference (records as <variable>MEETME_RECORDINGFILE</variable>
00189                   using format <variable>MEETME_RECORDINGFORMAT</variable>. Default filename is
00190                   <literal>meetme-conf-rec-${CONFNO}-${UNIQUEID}</literal> and the default format is
00191                   wav.</para>
00192                </option>
00193                <option name="s">
00194                   <para>Present menu (user or admin) when <literal>*</literal> is received
00195                   (send to menu).</para>
00196                </option>
00197                <option name="t">
00198                   <para>Set talk only mode. (Talk only, no listening).</para>
00199                </option>
00200                <option name="T">
00201                   <para>Set talker detection (sent to manager interface and meetme list).</para>
00202                </option>
00203                <option name="v" hasparams="optional">
00204                   <para>Announce when a user is joining or leaving the conference.  Use the voicemail greeting as the announcement.
00205                    If the i or I options are set, the application will fall back to them if no voicemail greeting can be found.</para>
00206                   <argument name="mailbox@[context]" required="true">
00207                      <para>The mailbox and voicemail context to play from.  If no context provided, assumed context is default.</para>
00208                   </argument>
00209                </option>
00210                <option name="w" hasparams="optional">
00211                   <para>Wait until the marked user enters the conference.</para>
00212                   <argument name="secs" required="true" />
00213                </option>
00214                <option name="x">
00215                   <para>Leave the conference when the last marked user leaves.</para>
00216                </option>
00217                <option name="X">
00218                   <para>Allow user to exit the conference by entering a valid single digit
00219                   extension <variable>MEETME_EXIT_CONTEXT</variable> or the current context
00220                   if that variable is not defined.</para>
00221                   <note>
00222                      <para>Option <literal>s</literal> has priority for <literal>*</literal>
00223                      since it cannot change its activation code.</para>
00224                   </note>
00225                </option>
00226                <option name="1">
00227                   <para>Do not play message when first person enters</para>
00228                </option>
00229                <option name="S">
00230                   <para>Kick the user <replaceable>x</replaceable> seconds <emphasis>after</emphasis> he entered into
00231                   the conference.</para>
00232                   <argument name="x" required="true" />
00233                </option>
00234                <option name="L" argsep=":">
00235                   <para>Limit the conference to <replaceable>x</replaceable> ms. Play a warning when
00236                   <replaceable>y</replaceable> ms are left. Repeat the warning every <replaceable>z</replaceable> ms.
00237                   The following special variables can be used with this option:</para>
00238                   <variablelist>
00239                      <variable name="CONF_LIMIT_TIMEOUT_FILE">
00240                         <para>File to play when time is up.</para>
00241                      </variable>
00242                      <variable name="CONF_LIMIT_WARNING_FILE">
00243                         <para>File to play as warning if <replaceable>y</replaceable> is defined. The
00244                         default is to say the time remaining.</para>
00245                      </variable>
00246                   </variablelist>
00247                   <argument name="x" />
00248                   <argument name="y" />
00249                   <argument name="z" />
00250                </option>
00251             </optionlist>
00252          </parameter>
00253          <parameter name="pin" />
00254       </syntax>
00255       <description>
00256          <para>Enters the user into a specified MeetMe conference.  If the <replaceable>confno</replaceable>
00257          is omitted, the user will be prompted to enter one.  User can exit the conference by hangup, or
00258          if the <literal>p</literal> option is specified, by pressing <literal>#</literal>.</para>
00259          <note><para>The DAHDI kernel modules and a functional DAHDI timing source (see dahdi_test)
00260          must be present for conferencing to operate properly. In addition, the chan_dahdi channel driver
00261          must be loaded for the <literal>i</literal> and <literal>r</literal> options to operate at
00262          all.</para></note>
00263       </description>
00264       <see-also>
00265          <ref type="application">MeetMeCount</ref>
00266          <ref type="application">MeetMeAdmin</ref>
00267          <ref type="application">MeetMeChannelAdmin</ref>
00268       </see-also>
00269    </application>
00270    <application name="MeetMeCount" language="en_US">
00271       <synopsis>
00272          MeetMe participant count.
00273       </synopsis>
00274       <syntax>
00275          <parameter name="confno" required="true">
00276             <para>Conference number.</para>
00277          </parameter>
00278          <parameter name="var" />
00279       </syntax>
00280       <description>
00281          <para>Plays back the number of users in the specified MeetMe conference.
00282          If <replaceable>var</replaceable> is specified, playback will be skipped and the value
00283          will be returned in the variable. Upon application completion, MeetMeCount will hangup
00284          the channel, unless priority <literal>n+1</literal> exists, in which case priority progress will
00285          continue.</para>
00286       </description>
00287       <see-also>
00288          <ref type="application">MeetMe</ref>
00289       </see-also>
00290    </application>
00291    <application name="MeetMeAdmin" language="en_US">
00292       <synopsis>
00293          MeetMe conference administration.
00294       </synopsis>
00295       <syntax>
00296          <parameter name="confno" required="true" />
00297          <parameter name="command" required="true">
00298             <optionlist>
00299                <option name="e">
00300                   <para>Eject last user that joined.</para>
00301                </option>
00302                <option name="E">
00303                   <para>Extend conference end time, if scheduled.</para>
00304                </option>
00305                <option name="k">
00306                   <para>Kick one user out of conference.</para>
00307                </option>
00308                <option name="K">
00309                   <para>Kick all users out of conference.</para>
00310                </option>
00311                <option name="l">
00312                   <para>Unlock conference.</para>
00313                </option>
00314                <option name="L">
00315                   <para>Lock conference.</para>
00316                </option>
00317                <option name="m">
00318                   <para>Unmute one user.</para>
00319                </option>
00320                <option name="M">
00321                   <para>Mute one user.</para>
00322                </option>
00323                <option name="n">
00324                   <para>Unmute all users in the conference.</para>
00325                </option>
00326                <option name="N">
00327                   <para>Mute all non-admin users in the conference.</para>
00328                </option>
00329                <option name="r">
00330                   <para>Reset one user's volume settings.</para>
00331                </option>
00332                <option name="R">
00333                   <para>Reset all users volume settings.</para>
00334                </option>
00335                <option name="s">
00336                   <para>Lower entire conference speaking volume.</para>
00337                </option>
00338                <option name="S">
00339                   <para>Raise entire conference speaking volume.</para>
00340                </option>
00341                <option name="t">
00342                   <para>Lower one user's talk volume.</para>
00343                </option>
00344                <option name="T">
00345                   <para>Raise one user's talk volume.</para>
00346                </option>
00347                <option name="u">
00348                   <para>Lower one user's listen volume.</para>
00349                </option>
00350                <option name="U">
00351                   <para>Raise one user's listen volume.</para>
00352                </option>
00353                <option name="v">
00354                   <para>Lower entire conference listening volume.</para>
00355                </option>
00356                <option name="V">
00357                   <para>Raise entire conference listening volume.</para>
00358                </option>
00359             </optionlist>
00360          </parameter>
00361          <parameter name="user" />
00362       </syntax>
00363       <description>
00364          <para>Run admin <replaceable>command</replaceable> for conference <replaceable>confno</replaceable>.</para>
00365          <para>Will additionally set the variable <variable>MEETMEADMINSTATUS</variable> with one of
00366          the following values:</para>
00367          <variablelist>
00368             <variable name="MEETMEADMINSTATUS">
00369                <value name="NOPARSE">
00370                   Invalid arguments.
00371                </value>
00372                <value name="NOTFOUND">
00373                   User specified was not found.
00374                </value>
00375                <value name="FAILED">
00376                   Another failure occurred.
00377                </value>
00378                <value name="OK">
00379                   The operation was completed successfully.
00380                </value>
00381             </variable>
00382          </variablelist>
00383       </description>
00384       <see-also>
00385          <ref type="application">MeetMe</ref>
00386       </see-also>
00387    </application>
00388    <application name="MeetMeChannelAdmin" language="en_US">
00389       <synopsis>
00390          MeetMe conference Administration (channel specific).
00391       </synopsis>
00392       <syntax>
00393          <parameter name="channel" required="true" />
00394          <parameter name="command" required="true">
00395             <optionlist>
00396                <option name="k">
00397                   <para>Kick the specified user out of the conference he is in.</para>
00398                </option>
00399                <option name="m">
00400                   <para>Unmute the specified user.</para>
00401                </option>
00402                <option name="M">
00403                   <para>Mute the specified user.</para>
00404                </option>
00405             </optionlist>
00406          </parameter>
00407       </syntax>
00408       <description>
00409          <para>Run admin <replaceable>command</replaceable> for a specific
00410          <replaceable>channel</replaceable> in any conference.</para>
00411       </description>
00412    </application>
00413    <application name="SLAStation" language="en_US">
00414       <synopsis>
00415          Shared Line Appearance Station.
00416       </synopsis>
00417       <syntax>
00418          <parameter name="station" required="true">
00419             <para>Station name</para>
00420          </parameter>
00421       </syntax>
00422       <description>
00423          <para>This application should be executed by an SLA station. The argument depends
00424          on how the call was initiated. If the phone was just taken off hook, then the argument
00425          <replaceable>station</replaceable> should be just the station name. If the call was
00426          initiated by pressing a line key, then the station name should be preceded by an underscore
00427          and the trunk name associated with that line button.</para>
00428          <para>For example: <literal>station1_line1</literal></para>
00429          <para>On exit, this application will set the variable <variable>SLASTATION_STATUS</variable> to
00430          one of the following values:</para>
00431          <variablelist>
00432             <variable name="SLASTATION_STATUS">
00433                <value name="FAILURE" />
00434                <value name="CONGESTION" />
00435                <value name="SUCCESS" />
00436             </variable>
00437          </variablelist>
00438       </description>
00439    </application>
00440    <application name="SLATrunk" language="en_US">
00441       <synopsis>
00442          Shared Line Appearance Trunk.
00443       </synopsis>
00444       <syntax>
00445          <parameter name="trunk" required="true">
00446             <para>Trunk name</para>
00447          </parameter>
00448          <parameter name="options">
00449             <optionlist>
00450                <option name="M" hasparams="optional">
00451                   <para>Play back the specified MOH <replaceable>class</replaceable>
00452                   instead of ringing</para>
00453                   <argument name="class" required="true" />
00454                </option>
00455             </optionlist>
00456          </parameter>
00457       </syntax>
00458       <description>
00459          <para>This application should be executed by an SLA trunk on an inbound call. The channel calling
00460          this application should correspond to the SLA trunk with the name <replaceable>trunk</replaceable>
00461          that is being passed as an argument.</para>
00462          <para>On exit, this application will set the variable <variable>SLATRUNK_STATUS</variable> to
00463          one of the following values:</para>
00464          <variablelist>
00465             <variable name="SLATRUNK_STATUS">
00466                <value name="FAILURE" />
00467                <value name="SUCCESS" />
00468                <value name="UNANSWERED" />
00469                <value name="RINGTIMEOUT" />
00470             </variable>
00471          </variablelist>
00472       </description>
00473    </application>
00474    <function name="MEETME_INFO" language="en_US">
00475       <synopsis>
00476          Query a given conference of various properties.
00477       </synopsis>
00478       <syntax>
00479          <parameter name="keyword" required="true">
00480             <para>Options:</para>
00481             <enumlist>
00482                <enum name="lock">
00483                   <para>Boolean of whether the corresponding conference is locked.</para>
00484                </enum>
00485                <enum name="parties">
00486                   <para>Number of parties in a given conference</para>
00487                </enum>
00488                <enum name="activity">
00489                   <para>Duration of conference in seconds.</para>
00490                </enum>
00491                <enum name="dynamic">
00492                   <para>Boolean of whether the corresponding conference is dynamic.</para>
00493                </enum>
00494             </enumlist>
00495          </parameter>
00496          <parameter name="confno" required="true">
00497             <para>Conference number to retrieve information from.</para>
00498          </parameter>
00499       </syntax>
00500       <description />
00501       <see-also>
00502          <ref type="application">MeetMe</ref>
00503          <ref type="application">MeetMeCount</ref>
00504          <ref type="application">MeetMeAdmin</ref>
00505          <ref type="application">MeetMeChannelAdmin</ref>
00506       </see-also>
00507    </function>
00508    <manager name="MeetmeMute" language="en_US">
00509       <synopsis>
00510          Mute a Meetme user.
00511       </synopsis>
00512       <syntax>
00513          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00514          <parameter name="Meetme" required="true" />
00515          <parameter name="Usernum" required="true" />
00516       </syntax>
00517       <description>
00518       </description>
00519    </manager>
00520    <manager name="MeetmeUnmute" language="en_US">
00521       <synopsis>
00522          Unmute a Meetme user.
00523       </synopsis>
00524       <syntax>
00525          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00526          <parameter name="Meetme" required="true" />
00527          <parameter name="Usernum" required="true" />
00528       </syntax>
00529       <description>
00530       </description>
00531    </manager>
00532    <manager name="MeetmeList" language="en_US">
00533       <synopsis>
00534          List participants in a conference.
00535       </synopsis>
00536       <syntax>
00537          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00538          <parameter name="Conference" required="false">
00539             <para>Conference number.</para>
00540          </parameter>
00541       </syntax>
00542       <description>
00543          <para>Lists all users in a particular MeetMe conference.
00544          MeetmeList will follow as separate events, followed by a final event called
00545          MeetmeListComplete.</para>
00546       </description>
00547    </manager>
00548    <manager name="MeetmeListRooms" language="en_US">
00549       <synopsis>
00550          List active conferences.
00551       </synopsis>
00552       <syntax>
00553          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00554       </syntax>
00555       <description>
00556          <para>Lists data about all active conferences.
00557             MeetmeListRooms will follow as separate events, followed by a final event called
00558             MeetmeListRoomsComplete.</para>
00559       </description>
00560    </manager>
00561    <managerEvent language="en_US" name="MeetmeJoin">
00562       <managerEventInstance class="EVENT_FLAG_CALL">
00563          <synopsis>Raised when a user joins a MeetMe conference.</synopsis>
00564          <syntax>
00565             <parameter name="Meetme">
00566                <para>The identifier for the MeetMe conference.</para>
00567             </parameter>
00568             <parameter name="Usernum">
00569                <para>The identifier of the MeetMe user who joined.</para>
00570             </parameter>
00571             <channel_snapshot/>
00572          </syntax>
00573          <see-also>
00574             <ref type="managerEvent">MeetmeLeave</ref>
00575             <ref type="application">MeetMe</ref>
00576          </see-also>
00577       </managerEventInstance>
00578    </managerEvent>
00579    <managerEvent language="en_US" name="MeetmeLeave">
00580       <managerEventInstance class="EVENT_FLAG_CALL">
00581          <synopsis>Raised when a user leaves a MeetMe conference.</synopsis>
00582          <syntax>
00583             <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
00584             <channel_snapshot/>
00585             <parameter name="Duration">
00586                <para>The length of time in seconds that the Meetme user was in the conference.</para>
00587             </parameter>
00588          </syntax>
00589          <see-also>
00590             <ref type="managerEvent">MeetmeJoin</ref>
00591          </see-also>
00592       </managerEventInstance>
00593    </managerEvent>
00594    <managerEvent language="en_US" name="MeetmeEnd">
00595       <managerEventInstance class="EVENT_FLAG_CALL">
00596          <synopsis>Raised when a MeetMe conference ends.</synopsis>
00597          <syntax>
00598             <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
00599          </syntax>
00600          <see-also>
00601             <ref type="managerEvent">MeetmeJoin</ref>
00602          </see-also>
00603       </managerEventInstance>
00604    </managerEvent>
00605    <managerEvent language="en_US" name="MeetmeTalkRequest">
00606       <managerEventInstance class="EVENT_FLAG_CALL">
00607          <synopsis>Raised when a MeetMe user has started talking.</synopsis>
00608          <syntax>
00609             <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
00610             <channel_snapshot/>
00611             <parameter name="Duration">
00612                <para>The length of time in seconds that the Meetme user has been in the conference at the time of this event.</para>
00613             </parameter>
00614             <parameter name="Status">
00615                <enumlist>
00616                   <enum name="on"/>
00617                   <enum name="off"/>
00618                </enumlist>
00619             </parameter>
00620          </syntax>
00621       </managerEventInstance>
00622    </managerEvent>
00623    <managerEvent language="en_US" name="MeetmeTalking">
00624       <managerEventInstance class="EVENT_FLAG_CALL">
00625          <synopsis>Raised when a MeetMe user begins or ends talking.</synopsis>
00626          <syntax>
00627             <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
00628             <channel_snapshot/>
00629             <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeTalkRequest']/managerEventInstance/syntax/parameter)" />
00630          </syntax>
00631       </managerEventInstance>
00632    </managerEvent>
00633    <managerEvent language="en_US" name="MeetmeMute">
00634       <managerEventInstance class="EVENT_FLAG_CALL">
00635          <synopsis>Raised when a MeetMe user is muted or unmuted.</synopsis>
00636          <syntax>
00637             <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter)" />
00638             <channel_snapshot/>
00639             <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeTalkRequest']/managerEventInstance/syntax/parameter)" />
00640          </syntax>
00641       </managerEventInstance>
00642    </managerEvent>
00643  ***/
00644 
00645 #define CONFIG_FILE_NAME   "meetme.conf"
00646 #define SLA_CONFIG_FILE    "sla.conf"
00647 #define STR_CONCISE        "concise"
00648 
00649 /*! each buffer is 20ms, so this is 640ms total */
00650 #define DEFAULT_AUDIO_BUFFERS  32
00651 
00652 /*! String format for scheduled conferences */
00653 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
00654 
00655 enum {
00656    ADMINFLAG_MUTED =     (1 << 1), /*!< User is muted */
00657    ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
00658    ADMINFLAG_KICKME =    (1 << 3),  /*!< User has been kicked */
00659    /*! User has requested to speak */
00660    ADMINFLAG_T_REQUEST = (1 << 4),
00661    ADMINFLAG_HANGUP = (1 << 5),  /*!< User will be leaving the conference */
00662 };
00663 
00664 #define MEETME_DELAYDETECTTALK     300
00665 #define MEETME_DELAYDETECTENDTALK  1000
00666 
00667 #define AST_FRAME_BITS  32
00668 
00669 enum volume_action {
00670    VOL_UP,
00671    VOL_DOWN
00672 };
00673 
00674 enum entrance_sound {
00675    ENTER,
00676    LEAVE
00677 };
00678 
00679 enum recording_state {
00680    MEETME_RECORD_OFF,
00681    MEETME_RECORD_STARTED,
00682    MEETME_RECORD_ACTIVE,
00683    MEETME_RECORD_TERMINATE
00684 };
00685 
00686 #define CONF_SIZE  320
00687 
00688 enum {
00689    /*! user has admin access on the conference */
00690    CONFFLAG_ADMIN = (1 << 0),
00691    /*! If set the user can only receive audio from the conference */
00692    CONFFLAG_MONITOR = (1 << 1),
00693    /*! If set asterisk will exit conference when key defined in p() option is pressed */
00694    CONFFLAG_KEYEXIT = (1 << 2),
00695    /*! If set asterisk will provide a menu to the user when '*' is pressed */
00696    CONFFLAG_STARMENU = (1 << 3),
00697    /*! If set the use can only send audio to the conference */
00698    CONFFLAG_TALKER = (1 << 4),
00699    /*! If set there will be no enter or leave sounds */
00700    CONFFLAG_QUIET = (1 << 5),
00701    /*! If set, when user joins the conference, they will be told the number 
00702     *  of users that are already in */
00703    CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
00704    /*! Set to run AGI Script in Background */
00705    CONFFLAG_AGI = (1 << 7),
00706    /*! Set to have music on hold when user is alone in conference */
00707    CONFFLAG_MOH = (1 << 8),
00708    /*! If set, the channel will leave the conference if all marked users leave */
00709    CONFFLAG_MARKEDEXIT = (1 << 9),
00710    /*! If set, the MeetMe will wait until a marked user enters */
00711    CONFFLAG_WAITMARKED = (1 << 10),
00712    /*! If set, the MeetMe will exit to the specified context */
00713    CONFFLAG_EXIT_CONTEXT = (1 << 11),
00714    /*! If set, the user will be marked */
00715    CONFFLAG_MARKEDUSER = (1 << 12),
00716    /*! If set, user will be ask record name on entry of conference */
00717    CONFFLAG_INTROUSER = (1 << 13),
00718    /*! If set, the MeetMe will be recorded */
00719    CONFFLAG_RECORDCONF = (1<< 14),
00720    /*! If set, the user will be monitored if the user is talking or not */
00721    CONFFLAG_MONITORTALKER = (1 << 15),
00722    CONFFLAG_DYNAMIC = (1 << 16),
00723    CONFFLAG_DYNAMICPIN = (1 << 17),
00724    CONFFLAG_EMPTY = (1 << 18),
00725    CONFFLAG_EMPTYNOPIN = (1 << 19),
00726    CONFFLAG_ALWAYSPROMPT = (1 << 20),
00727    /*! If set, treat talking users as muted users */
00728    CONFFLAG_OPTIMIZETALKER = (1 << 21),
00729    /*! If set, won't speak the extra prompt when the first person 
00730     *  enters the conference */
00731    CONFFLAG_NOONLYPERSON = (1 << 22),
00732    /*! If set, user will be asked to record name on entry of conference 
00733     *  without review */
00734    CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
00735    /*! If set, the user will be initially self-muted */
00736    CONFFLAG_STARTMUTED = (1 << 24),
00737    /*! Pass DTMF through the conference */
00738    CONFFLAG_PASS_DTMF = (1 << 25),
00739    CONFFLAG_SLA_STATION = (1 << 26),
00740    CONFFLAG_SLA_TRUNK = (1 << 27),
00741    /*! If set, the user should continue in the dialplan if kicked out */
00742    CONFFLAG_KICK_CONTINUE = (1 << 28),
00743    CONFFLAG_DURATION_STOP = (1 << 29),
00744    CONFFLAG_DURATION_LIMIT = (1 << 30),
00745 };
00746 
00747 /* These flags are defined separately because we ran out of bits that an enum can be used to represent. 
00748    If you add new flags, be sure to do it in the same way that these are. */
00749 /*! Do not write any audio to this channel until the state is up. */
00750 #define CONFFLAG_NO_AUDIO_UNTIL_UP  (1ULL << 31)
00751 #define CONFFLAG_INTROMSG           (1ULL << 32) /*!< If set play an intro announcement at start of conference */
00752 #define CONFFLAG_INTROUSER_VMREC    (1ULL << 33)
00753 /*! If there's only one person left in a conference when someone leaves, kill the conference */
00754 #define CONFFLAG_KILL_LAST_MAN_STANDING (1ULL << 34)
00755 /*! If set, don't enable a denoiser for the channel */
00756 #define CONFFLAG_DONT_DENOISE       (1ULL << 35)
00757 
00758 enum {
00759    OPT_ARG_WAITMARKED = 0,
00760    OPT_ARG_EXITKEYS   = 1,
00761    OPT_ARG_DURATION_STOP = 2,
00762    OPT_ARG_DURATION_LIMIT = 3,
00763    OPT_ARG_MOH_CLASS = 4,
00764    OPT_ARG_INTROMSG = 5,
00765    OPT_ARG_INTROUSER_VMREC = 6,
00766    OPT_ARG_ARRAY_SIZE = 7,
00767 };
00768 
00769 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
00770    AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
00771    AST_APP_OPTION('a', CONFFLAG_ADMIN ),
00772    AST_APP_OPTION('b', CONFFLAG_AGI ),
00773    AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
00774    AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
00775    AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
00776    AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
00777    AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
00778    AST_APP_OPTION('e', CONFFLAG_EMPTY ),
00779    AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
00780    AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
00781    AST_APP_OPTION_ARG('v', CONFFLAG_INTROUSER_VMREC , OPT_ARG_INTROUSER_VMREC),
00782    AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
00783    AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
00784    AST_APP_OPTION('k', CONFFLAG_KILL_LAST_MAN_STANDING ),
00785    AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
00786    AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
00787    AST_APP_OPTION('n', CONFFLAG_DONT_DENOISE ),
00788    AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
00789    AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
00790    AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
00791    AST_APP_OPTION('q', CONFFLAG_QUIET ),
00792    AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
00793    AST_APP_OPTION('s', CONFFLAG_STARMENU ),
00794    AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
00795    AST_APP_OPTION('l', CONFFLAG_MONITOR ),
00796    AST_APP_OPTION('t', CONFFLAG_TALKER ),
00797    AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
00798    AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
00799    AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
00800    AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
00801    AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
00802    AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
00803 END_OPTIONS );
00804 
00805 static const char * const app = "MeetMe";
00806 static const char * const app2 = "MeetMeCount";
00807 static const char * const app3 = "MeetMeAdmin";
00808 static const char * const app4 = "MeetMeChannelAdmin";
00809 static const char * const slastation_app = "SLAStation";
00810 static const char * const slatrunk_app = "SLATrunk";
00811 
00812 /* Lookup RealTime conferences based on confno and current time */
00813 static int rt_schedule;
00814 static int fuzzystart;
00815 static int earlyalert;
00816 static int endalert;
00817 static int extendby;
00818 
00819 /*! Log participant count to the RealTime backend */
00820 static int rt_log_members;
00821 
00822 #define MAX_CONFNUM 80
00823 #define MAX_PIN     80
00824 #define OPTIONS_LEN 100
00825 
00826 /* Enough space for "<conference #>,<pin>,<admin pin>" followed by a 0 byte. */
00827 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
00828 
00829 enum announcetypes {
00830    CONF_HASJOIN,
00831    CONF_HASLEFT
00832 };
00833 
00834 struct announce_listitem {
00835    AST_LIST_ENTRY(announce_listitem) entry;
00836    char namerecloc[PATH_MAX];          /*!< Name Recorded file Location */
00837    char language[MAX_LANGUAGE];
00838    struct ast_channel *confchan;
00839    int confusers;
00840    int vmrec;
00841    enum announcetypes announcetype;
00842 };
00843 
00844 /*! \brief The MeetMe Conference object */
00845 struct ast_conference {
00846    ast_mutex_t playlock;                   /*!< Conference specific lock (players) */
00847    ast_mutex_t listenlock;                 /*!< Conference specific lock (listeners) */
00848    char confno[MAX_CONFNUM];               /*!< Conference */
00849    struct ast_channel *chan;               /*!< Announcements channel */
00850    struct ast_channel *lchan;              /*!< Listen/Record channel */
00851    int fd;                                 /*!< Announcements fd */
00852    int dahdiconf;                            /*!< DAHDI Conf # */
00853    int users;                              /*!< Number of active users */
00854    int markedusers;                        /*!< Number of marked users */
00855    int maxusers;                           /*!< Participant limit if scheduled */
00856    int endalert;                           /*!< When to play conf ending message */
00857    time_t start;                           /*!< Start time (s) */
00858    int refcount;                           /*!< reference count of usage */
00859    enum recording_state recording:2;       /*!< recording status */
00860    unsigned int isdynamic:1;               /*!< Created on the fly? */
00861    unsigned int locked:1;                  /*!< Is the conference locked? */
00862    unsigned int gmuted:1;                  /*!< Is the conference globally muted? (all non-admins) */
00863    pthread_t recordthread;                 /*!< thread for recording */
00864    ast_mutex_t recordthreadlock;           /*!< control threads trying to start recordthread */
00865    pthread_attr_t attr;                    /*!< thread attribute */
00866    char *recordingfilename;                /*!< Filename to record the Conference into */
00867    char *recordingformat;                  /*!< Format to record the Conference in */
00868    char pin[MAX_PIN];                      /*!< If protected by a PIN */
00869    char pinadmin[MAX_PIN];                 /*!< If protected by a admin PIN */
00870    char uniqueid[32];
00871    long endtime;                           /*!< When to end the conf if scheduled */
00872    const char *useropts;                   /*!< RealTime user flags */
00873    const char *adminopts;                  /*!< RealTime moderator flags */
00874    const char *bookid;                     /*!< RealTime conference id */
00875    struct ast_frame *transframe[32];
00876    struct ast_frame *origframe;
00877    struct ast_trans_pvt *transpath[32];
00878    struct ao2_container *usercontainer;
00879    AST_LIST_ENTRY(ast_conference) list;
00880    /* announce_thread related data */
00881    pthread_t announcethread;
00882    ast_mutex_t announcethreadlock;
00883    unsigned int announcethread_stop:1;
00884    ast_cond_t announcelist_addition;
00885    AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
00886    ast_mutex_t announcelistlock;
00887 };
00888 
00889 static AST_LIST_HEAD_STATIC(confs, ast_conference);
00890 
00891 static unsigned int conf_map[1024] = {0, };
00892 
00893 struct volume {
00894    int desired;                            /*!< Desired volume adjustment */
00895    int actual;                             /*!< Actual volume adjustment (for channels that can't adjust) */
00896 };
00897 
00898 /*! \brief The MeetMe User object */
00899 struct ast_conf_user {
00900    int user_no;                            /*!< User Number */
00901    struct ast_flags64 userflags;           /*!< Flags as set in the conference */
00902    int adminflags;                         /*!< Flags set by the Admin */
00903    struct ast_channel *chan;               /*!< Connected channel */
00904    int talking;                            /*!< Is user talking */
00905    int dahdichannel;                       /*!< Is a DAHDI channel */
00906    char usrvalue[50];                      /*!< Custom User Value */
00907    char namerecloc[PATH_MAX];    /*!< Name Recorded file Location */
00908    time_t jointime;                        /*!< Time the user joined the conference */
00909    time_t kicktime;                        /*!< Time the user will be kicked from the conference */
00910    struct timeval start_time;              /*!< Time the user entered into the conference */
00911    long timelimit;                         /*!< Time limit for the user to be in the conference L(x:y:z) */
00912    long play_warning;                      /*!< Play a warning when 'y' ms are left */
00913    long warning_freq;                      /*!< Repeat the warning every 'z' ms */
00914    const char *warning_sound;              /*!< File to play as warning if 'y' is defined */
00915    const char *end_sound;                  /*!< File to play when time is up. */
00916    struct volume talk;
00917    struct volume listen;
00918    AST_LIST_ENTRY(ast_conf_user) list;
00919 };
00920 
00921 enum sla_which_trunk_refs {
00922    ALL_TRUNK_REFS,
00923    INACTIVE_TRUNK_REFS,
00924 };
00925 
00926 enum sla_trunk_state {
00927    SLA_TRUNK_STATE_IDLE,
00928    SLA_TRUNK_STATE_RINGING,
00929    SLA_TRUNK_STATE_UP,
00930    SLA_TRUNK_STATE_ONHOLD,
00931    SLA_TRUNK_STATE_ONHOLD_BYME,
00932 };
00933 
00934 enum sla_hold_access {
00935    /*! This means that any station can put it on hold, and any station
00936     * can retrieve the call from hold. */
00937    SLA_HOLD_OPEN,
00938    /*! This means that only the station that put the call on hold may
00939     * retrieve it from hold. */
00940    SLA_HOLD_PRIVATE,
00941 };
00942 
00943 struct sla_trunk_ref;
00944 
00945 struct sla_station {
00946    AST_RWLIST_ENTRY(sla_station) entry;
00947    AST_DECLARE_STRING_FIELDS(
00948       AST_STRING_FIELD(name); 
00949       AST_STRING_FIELD(device);  
00950       AST_STRING_FIELD(autocontext);   
00951    );
00952    AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
00953    struct ast_dial *dial;
00954    /*! Ring timeout for this station, for any trunk.  If a ring timeout
00955     *  is set for a specific trunk on this station, that will take
00956     *  priority over this value. */
00957    unsigned int ring_timeout;
00958    /*! Ring delay for this station, for any trunk.  If a ring delay
00959     *  is set for a specific trunk on this station, that will take
00960     *  priority over this value. */
00961    unsigned int ring_delay;
00962    /*! This option uses the values in the sla_hold_access enum and sets the
00963     * access control type for hold on this station. */
00964    unsigned int hold_access:1;
00965    /*! Mark used during reload processing */
00966    unsigned int mark:1;
00967 };
00968 
00969 /*!
00970  * \brief A reference to a station
00971  *
00972  * This struct looks near useless at first glance.  However, its existence
00973  * in the list of stations in sla_trunk means that this station references
00974  * that trunk.  We use the mark to keep track of whether it needs to be
00975  * removed from the sla_trunk's list of stations during a reload.
00976  */
00977 struct sla_station_ref {
00978    AST_LIST_ENTRY(sla_station_ref) entry;
00979    struct sla_station *station;
00980    /*! Mark used during reload processing */
00981    unsigned int mark:1;
00982 };
00983 
00984 struct sla_trunk {
00985    AST_DECLARE_STRING_FIELDS(
00986       AST_STRING_FIELD(name);
00987       AST_STRING_FIELD(device);
00988       AST_STRING_FIELD(autocontext);   
00989    );
00990    AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
00991    /*! Number of stations that use this trunk */
00992    unsigned int num_stations;
00993    /*! Number of stations currently on a call with this trunk */
00994    unsigned int active_stations;
00995    /*! Number of stations that have this trunk on hold. */
00996    unsigned int hold_stations;
00997    struct ast_channel *chan;
00998    unsigned int ring_timeout;
00999    /*! If set to 1, no station will be able to join an active call with
01000     *  this trunk. */
01001    unsigned int barge_disabled:1;
01002    /*! This option uses the values in the sla_hold_access enum and sets the
01003     * access control type for hold on this trunk. */
01004    unsigned int hold_access:1;
01005    /*! Whether this trunk is currently on hold, meaning that once a station
01006     *  connects to it, the trunk channel needs to have UNHOLD indicated to it. */
01007    unsigned int on_hold:1;
01008    /*! Mark used during reload processing */
01009    unsigned int mark:1;
01010 };
01011 
01012 /*!
01013  * \brief A station's reference to a trunk
01014  *
01015  * An sla_station keeps a list of trunk_refs.  This holds metadata about the
01016  * stations usage of the trunk.
01017  */
01018 struct sla_trunk_ref {
01019    AST_LIST_ENTRY(sla_trunk_ref) entry;
01020    struct sla_trunk *trunk;
01021    enum sla_trunk_state state;
01022    struct ast_channel *chan;
01023    /*! Ring timeout to use when this trunk is ringing on this specific
01024     *  station.  This takes higher priority than a ring timeout set at
01025     *  the station level. */
01026    unsigned int ring_timeout;
01027    /*! Ring delay to use when this trunk is ringing on this specific
01028     *  station.  This takes higher priority than a ring delay set at
01029     *  the station level. */
01030    unsigned int ring_delay;
01031    /*! Mark used during reload processing */
01032    unsigned int mark:1;
01033 };
01034 
01035 static struct ao2_container *sla_stations;
01036 static struct ao2_container *sla_trunks;
01037 
01038 static const char sla_registrar[] = "SLA";
01039 
01040 /*! \brief Event types that can be queued up for the SLA thread */
01041 enum sla_event_type {
01042    /*! A station has put the call on hold */
01043    SLA_EVENT_HOLD,
01044    /*! The state of a dial has changed */
01045    SLA_EVENT_DIAL_STATE,
01046    /*! The state of a ringing trunk has changed */
01047    SLA_EVENT_RINGING_TRUNK,
01048 };
01049 
01050 struct sla_event {
01051    enum sla_event_type type;
01052    struct sla_station *station;
01053    struct sla_trunk_ref *trunk_ref;
01054    AST_LIST_ENTRY(sla_event) entry;
01055 };
01056 
01057 /*! \brief A station that failed to be dialed 
01058  * \note Only used by the SLA thread. */
01059 struct sla_failed_station {
01060    struct sla_station *station;
01061    struct timeval last_try;
01062    AST_LIST_ENTRY(sla_failed_station) entry;
01063 };
01064 
01065 /*! \brief A trunk that is ringing */
01066 struct sla_ringing_trunk {
01067    struct sla_trunk *trunk;
01068    /*! The time that this trunk started ringing */
01069    struct timeval ring_begin;
01070    AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
01071    AST_LIST_ENTRY(sla_ringing_trunk) entry;
01072 };
01073 
01074 enum sla_station_hangup {
01075    SLA_STATION_HANGUP_NORMAL,
01076    SLA_STATION_HANGUP_TIMEOUT,
01077 };
01078 
01079 /*! \brief A station that is ringing */
01080 struct sla_ringing_station {
01081    struct sla_station *station;
01082    /*! The time that this station started ringing */
01083    struct timeval ring_begin;
01084    AST_LIST_ENTRY(sla_ringing_station) entry;
01085 };
01086 
01087 /*!
01088  * \brief A structure for data used by the sla thread
01089  */
01090 static struct {
01091    /*! The SLA thread ID */
01092    pthread_t thread;
01093    ast_cond_t cond;
01094    ast_mutex_t lock;
01095    AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
01096    AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
01097    AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
01098    AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
01099    unsigned int stop:1;
01100    /*! Attempt to handle CallerID, even though it is known not to work
01101     *  properly in some situations. */
01102    unsigned int attempt_callerid:1;
01103 } sla = {
01104    .thread = AST_PTHREADT_NULL,
01105 };
01106 
01107 /*! \brief The number of audio buffers to be allocated on pseudo channels
01108  *  when in a conference */
01109 static int audio_buffers;
01110 
01111 /*! \brief Map 'volume' levels from -5 through +5 into decibel (dB) 
01112  *    settings for channel drivers.
01113  *
01114  *  \note these are not a straight linear-to-dB
01115  *  conversion... the numbers have been modified
01116  *  to give the user a better level of adjustability.
01117  */
01118 static const char gain_map[] = {
01119    -15,
01120    -13,
01121    -10,
01122    -6,
01123    0,
01124    0,
01125    0,
01126    6,
01127    10,
01128    13,
01129    15,
01130 };
01131 
01132 /* Routes the various meetme message types to the meetme stasis callback function to turn them into events */
01133 static struct stasis_message_router *meetme_event_message_router;
01134 
01135 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_join_type);
01136 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_leave_type);
01137 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_end_type);
01138 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_mute_type);
01139 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_talking_type);
01140 STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_talk_request_type);
01141 
01142 static void meetme_stasis_cb(void *data, struct stasis_subscription *sub,
01143    struct stasis_message *message);
01144 
01145 static void meetme_stasis_cleanup(void)
01146 {
01147    if (meetme_event_message_router) {
01148       stasis_message_router_unsubscribe(meetme_event_message_router);
01149       meetme_event_message_router = NULL;
01150    }
01151 
01152    STASIS_MESSAGE_TYPE_CLEANUP(meetme_join_type);
01153    STASIS_MESSAGE_TYPE_CLEANUP(meetme_leave_type);
01154    STASIS_MESSAGE_TYPE_CLEANUP(meetme_end_type);
01155    STASIS_MESSAGE_TYPE_CLEANUP(meetme_mute_type);
01156    STASIS_MESSAGE_TYPE_CLEANUP(meetme_talking_type);
01157    STASIS_MESSAGE_TYPE_CLEANUP(meetme_talk_request_type);
01158 }
01159 
01160 static int meetme_stasis_init(void)
01161 {
01162 
01163    STASIS_MESSAGE_TYPE_INIT(meetme_join_type);
01164    STASIS_MESSAGE_TYPE_INIT(meetme_leave_type);
01165    STASIS_MESSAGE_TYPE_INIT(meetme_end_type);
01166    STASIS_MESSAGE_TYPE_INIT(meetme_mute_type);
01167    STASIS_MESSAGE_TYPE_INIT(meetme_talking_type);
01168    STASIS_MESSAGE_TYPE_INIT(meetme_talk_request_type);
01169 
01170    meetme_event_message_router = stasis_message_router_create(
01171       ast_channel_topic_all_cached());
01172 
01173    if (!meetme_event_message_router) {
01174       meetme_stasis_cleanup();
01175       return -1;
01176    }
01177 
01178    if (stasis_message_router_add(meetme_event_message_router,
01179          meetme_join_type(),
01180          meetme_stasis_cb,
01181          NULL)) {
01182       meetme_stasis_cleanup();
01183       return -1;
01184    }
01185 
01186    if (stasis_message_router_add(meetme_event_message_router,
01187          meetme_leave_type(),
01188          meetme_stasis_cb,
01189          NULL)) {
01190       meetme_stasis_cleanup();
01191       return -1;
01192    }
01193 
01194    if (stasis_message_router_add(meetme_event_message_router,
01195          meetme_end_type(),
01196          meetme_stasis_cb,
01197          NULL)) {
01198       meetme_stasis_cleanup();
01199       return -1;
01200    }
01201 
01202    if (stasis_message_router_add(meetme_event_message_router,
01203          meetme_mute_type(),
01204          meetme_stasis_cb,
01205          NULL)) {
01206       meetme_stasis_cleanup();
01207       return -1;
01208    }
01209 
01210    if (stasis_message_router_add(meetme_event_message_router,
01211          meetme_talking_type(),
01212          meetme_stasis_cb,
01213          NULL)) {
01214       meetme_stasis_cleanup();
01215       return -1;
01216    }
01217 
01218    if (stasis_message_router_add(meetme_event_message_router,
01219          meetme_talk_request_type(),
01220          meetme_stasis_cb,
01221          NULL)) {
01222       meetme_stasis_cleanup();
01223       return -1;
01224    }
01225 
01226    return 0;
01227 }
01228 
01229 static void meetme_stasis_cb(void *data, struct stasis_subscription *sub,
01230    struct stasis_message *message)
01231 {
01232    struct ast_channel_blob *channel_blob = stasis_message_data(message);
01233    struct stasis_message_type *message_type;
01234    const char *event;
01235    const char *conference_num;
01236    const char *status;
01237    struct ast_json *json_cur;
01238    RAII_VAR(struct ast_str *, channel_text, NULL, ast_free);
01239    RAII_VAR(struct ast_str *, extra_text, NULL, ast_free);
01240 
01241    if (!channel_blob) {
01242       ast_assert(0);
01243       return;
01244    }
01245 
01246    message_type = stasis_message_type(message);
01247 
01248    if (!message_type) {
01249       ast_assert(0);
01250       return;
01251    }
01252 
01253    if (message_type == meetme_join_type()) {
01254       event = "MeetmeJoin";
01255    } else if (message_type == meetme_leave_type()) {
01256       event = "MeetmeLeave";
01257    } else if (message_type == meetme_end_type()) {
01258       event = "MeetmeEnd";
01259    } else if (message_type == meetme_mute_type()) {
01260       event = "MeetmeMute";
01261    } else if (message_type == meetme_talking_type()) {
01262       event = "MeetmeTalking";
01263    } else if (message_type == meetme_talk_request_type()) {
01264       event = "MeetmeTalkRequest";
01265    } else {
01266       ast_assert(0);
01267       return;
01268    }
01269 
01270    if (!event) {
01271       ast_assert(0);
01272       return;
01273    }
01274 
01275    conference_num = ast_json_string_get(ast_json_object_get(channel_blob->blob, "Meetme"));
01276    if (!conference_num) {
01277       ast_assert(0);
01278       return;
01279    }
01280 
01281    status = ast_json_string_get(ast_json_object_get(channel_blob->blob, "status"));
01282    if (status) {
01283       ast_str_append_event_header(&extra_text, "Status", status);
01284    }
01285 
01286    if (channel_blob->snapshot) {
01287       channel_text = ast_manager_build_channel_state_string(channel_blob->snapshot);
01288    }
01289 
01290    if ((json_cur = ast_json_object_get(channel_blob->blob, "user"))) {
01291       int user_number = ast_json_integer_get(json_cur);
01292       RAII_VAR(struct ast_str *, user_prop_str, ast_str_create(32), ast_free);
01293       if (!user_prop_str) {
01294          return;
01295       }
01296 
01297       ast_str_set(&user_prop_str, 0, "%d", user_number);
01298       ast_str_append_event_header(&extra_text, "User", ast_str_buffer(user_prop_str));
01299 
01300       if ((json_cur = ast_json_object_get(channel_blob->blob, "duration"))) {
01301          int duration = ast_json_integer_get(json_cur);
01302          ast_str_set(&user_prop_str, 0, "%d", duration);
01303          ast_str_append_event_header(&extra_text, "Duration", ast_str_buffer(user_prop_str));
01304       }
01305 
01306       json_cur = NULL;
01307    }
01308 
01309    manager_event(EVENT_FLAG_CALL, event,
01310       "Meetme: %s\r\n"
01311       "%s"
01312       "%s",
01313       conference_num,
01314       channel_text ? ast_str_buffer(channel_text) : "",
01315       extra_text ? ast_str_buffer(extra_text) : "");
01316 }
01317 
01318 /*!
01319  * \internal
01320  * \brief Build a json object from a status value for inclusion in json extras for meetme_stasis_generate_msg
01321  * \since 12.0.0
01322  *
01323  * \param on if true, then status is on. Otherwise status is off
01324  * \retval NULL on failure to allocate the JSON blob.
01325  * \retval pointer to the JSON blob if successful.
01326  */
01327 static struct ast_json *status_to_json(int on)
01328 {
01329    struct ast_json *json_object = ast_json_pack("{s: s}",
01330       "status", on ? "on" : "off");
01331 
01332    return json_object;
01333 }
01334 
01335 /*!
01336  * \internal
01337  * \brief Generate a stasis message associated with a meetme event
01338  * \since 12.0.0
01339  *
01340  * \param meetme_confere The conference responsible for generating this message
01341  * \param chan The channel involved in the message (NULL allowed)
01342  * \param user The conference user involved in the message (NULL allowed)
01343  * \param message_type the type the stasis message being generated
01344  * \param extras Additional json fields desired for inclusion
01345  */
01346 static void meetme_stasis_generate_msg(struct ast_conference *meetme_conference, struct ast_channel *chan,
01347    struct ast_conf_user *user, struct stasis_message_type *message_type, struct ast_json *extras)
01348 {
01349    RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
01350    RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
01351 
01352    json_object = ast_json_pack("{s: s}",
01353       "Meetme", meetme_conference->confno);
01354 
01355    if (!json_object) {
01356       return;
01357    }
01358 
01359    if (extras) {
01360       ast_json_object_update(json_object, extras);
01361    }
01362 
01363    if (user) {
01364       struct timeval now = ast_tvnow();
01365       long duration = (long)(now.tv_sec - user->jointime);
01366       struct ast_json *json_user;
01367       struct ast_json *json_user_duration;
01368 
01369       json_user = ast_json_integer_create(user->user_no);
01370       if (!json_user || ast_json_object_set(json_object, "user", json_user)) {
01371          return;
01372       }
01373 
01374       if (duration > 0) {
01375          json_user_duration = ast_json_integer_create(duration);
01376          if (!json_user_duration
01377             || ast_json_object_set(json_object, "duration", json_user_duration)) {
01378             return;
01379          }
01380       }
01381    }
01382 
01383    if (chan) {
01384       ast_channel_lock(chan);
01385    }
01386    msg = ast_channel_blob_create(chan, message_type, json_object);
01387    if (chan) {
01388       ast_channel_unlock(chan);
01389    }
01390 
01391    if (!msg) {
01392       return;
01393    }
01394 
01395    stasis_publish(ast_channel_topic(chan), msg);
01396 }
01397 
01398 static int admin_exec(struct ast_channel *chan, const char *data);
01399 static void *recordthread(void *args);
01400 
01401 static const char *istalking(int x)
01402 {
01403    if (x > 0)
01404       return "(talking)";
01405    else if (x < 0)
01406       return "(unmonitored)";
01407    else 
01408       return "(not talking)";
01409 }
01410 
01411 static int careful_write(int fd, unsigned char *data, int len, int block)
01412 {
01413    int res;
01414    int x;
01415 
01416    while (len) {
01417       if (block) {
01418          x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
01419          res = ioctl(fd, DAHDI_IOMUX, &x);
01420       } else
01421          res = 0;
01422       if (res >= 0)
01423          res = write(fd, data, len);
01424       if (res < 1) {
01425          if (errno != EAGAIN) {
01426             ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
01427             return -1;
01428          } else
01429             return 0;
01430       }
01431       len -= res;
01432       data += res;
01433    }
01434 
01435    return 0;
01436 }
01437 
01438 static int set_talk_volume(struct ast_conf_user *user, int volume)
01439 {
01440    char gain_adjust;
01441 
01442    /* attempt to make the adjustment in the channel driver;
01443       if successful, don't adjust in the frame reading routine
01444    */
01445    gain_adjust = gain_map[volume + 5];
01446 
01447    return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
01448 }
01449 
01450 static int set_listen_volume(struct ast_conf_user *user, int volume)
01451 {
01452    char gain_adjust;
01453 
01454    /* attempt to make the adjustment in the channel driver;
01455       if successful, don't adjust in the frame reading routine
01456    */
01457    gain_adjust = gain_map[volume + 5];
01458 
01459    return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
01460 }
01461 
01462 static void tweak_volume(struct volume *vol, enum volume_action action)
01463 {
01464    switch (action) {
01465    case VOL_UP:
01466       switch (vol->desired) { 
01467       case 5:
01468          break;
01469       case 0:
01470          vol->desired = 2;
01471          break;
01472       case -2:
01473          vol->desired = 0;
01474          break;
01475       default:
01476          vol->desired++;
01477          break;
01478       }
01479       break;
01480    case VOL_DOWN:
01481       switch (vol->desired) {
01482       case -5:
01483          break;
01484       case 2:
01485          vol->desired = 0;
01486          break;
01487       case 0:
01488          vol->desired = -2;
01489          break;
01490       default:
01491          vol->desired--;
01492          break;
01493       }
01494    }
01495 }
01496 
01497 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
01498 {
01499    tweak_volume(&user->talk, action);
01500    /* attempt to make the adjustment in the channel driver;
01501       if successful, don't adjust in the frame reading routine
01502    */
01503    if (!set_talk_volume(user, user->talk.desired))
01504       user->talk.actual = 0;
01505    else
01506       user->talk.actual = user->talk.desired;
01507 }
01508 
01509 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
01510 {
01511    tweak_volume(&user->listen, action);
01512    /* attempt to make the adjustment in the channel driver;
01513       if successful, don't adjust in the frame reading routine
01514    */
01515    if (!set_listen_volume(user, user->listen.desired))
01516       user->listen.actual = 0;
01517    else
01518       user->listen.actual = user->listen.desired;
01519 }
01520 
01521 static void reset_volumes(struct ast_conf_user *user)
01522 {
01523    signed char zero_volume = 0;
01524 
01525    ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
01526    ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
01527 }
01528 
01529 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
01530 {
01531    unsigned char *data;
01532    int len;
01533    int res = -1;
01534 
01535    ast_test_suite_event_notify("CONFPLAY", "Channel: %s\r\n"
01536       "Conference: %s\r\n"
01537       "Marked: %d",
01538       ast_channel_name(chan),
01539       conf->confno,
01540       conf->markedusers);
01541 
01542    if (!ast_check_hangup(chan))
01543       res = ast_autoservice_start(chan);
01544 
01545    AST_LIST_LOCK(&confs);
01546 
01547    switch(sound) {
01548    case ENTER:
01549       data = enter;
01550       len = sizeof(enter);
01551       break;
01552    case LEAVE:
01553       data = leave;
01554       len = sizeof(leave);
01555       break;
01556    default:
01557       data = NULL;
01558       len = 0;
01559    }
01560    if (data) {
01561       careful_write(conf->fd, data, len, 1);
01562    }
01563 
01564    AST_LIST_UNLOCK(&confs);
01565 
01566    if (!res) 
01567       ast_autoservice_stop(chan);
01568 }
01569 
01570 static int user_no_cmp(void *obj, void *arg, int flags)
01571 {
01572    struct ast_conf_user *user = obj;
01573    int *user_no = arg;
01574 
01575    if (user->user_no == *user_no) {
01576       return (CMP_MATCH | CMP_STOP);
01577    }
01578 
01579    return 0;
01580 }
01581 
01582 static int user_max_cmp(void *obj, void *arg, int flags)
01583 {
01584    struct ast_conf_user *user = obj;
01585    int *max_no = arg;
01586 
01587    if (user->user_no > *max_no) {
01588       *max_no = user->user_no;
01589    }
01590 
01591    return 0;
01592 }
01593 
01594 /*!
01595  * \brief Find or create a conference
01596  *
01597  * \param confno The conference name/number
01598  * \param pin The regular user pin
01599  * \param pinadmin The admin pin
01600  * \param make Make the conf if it doesn't exist
01601  * \param dynamic Mark the newly created conference as dynamic
01602  * \param refcount How many references to mark on the conference
01603  * \param chan The asterisk channel
01604  * \param test
01605  *
01606  * \return A pointer to the conference struct, or NULL if it wasn't found and
01607  *         make or dynamic were not set.
01608  */
01609 static struct ast_conference *build_conf(const char *confno, const char *pin,
01610    const char *pinadmin, int make, int dynamic, int refcount,
01611    const struct ast_channel *chan, struct ast_test *test)
01612 {
01613    struct ast_conference *cnf;
01614    struct dahdi_confinfo dahdic = { 0, };
01615    int confno_int = 0;
01616    struct ast_format_cap *cap_slin = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
01617 
01618    AST_LIST_LOCK(&confs);
01619 
01620    AST_LIST_TRAVERSE(&confs, cnf, list) {
01621       if (!strcmp(confno, cnf->confno)) 
01622          break;
01623    }
01624 
01625    if (cnf || (!make && !dynamic) || !cap_slin)
01626       goto cnfout;
01627 
01628    ast_format_cap_append(cap_slin, ast_format_slin, 0);
01629    /* Make a new one */
01630    if (!(cnf = ast_calloc(1, sizeof(*cnf))) ||
01631       !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) {
01632       goto cnfout;
01633    }
01634 
01635    ast_mutex_init(&cnf->playlock);
01636    ast_mutex_init(&cnf->listenlock);
01637    cnf->recordthread = AST_PTHREADT_NULL;
01638    ast_mutex_init(&cnf->recordthreadlock);
01639    cnf->announcethread = AST_PTHREADT_NULL;
01640    ast_mutex_init(&cnf->announcethreadlock);
01641    ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
01642    ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
01643    ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
01644    ast_copy_string(cnf->uniqueid, ast_channel_uniqueid(chan), sizeof(cnf->uniqueid));
01645 
01646    /* Setup a new dahdi conference */
01647    dahdic.confno = -1;
01648    dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
01649    cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
01650    if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
01651       if (test) {
01652          /* if we are creating a conference for a unit test, it is not neccesary
01653           * to open a pseudo channel, so, if we fail continue creating
01654           * the conference. */
01655          ast_test_status_update(test, "Unable to open DAHDI pseudo device\n");
01656       } else {
01657          ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
01658          if (cnf->fd >= 0)
01659             close(cnf->fd);
01660          ao2_ref(cnf->usercontainer, -1);
01661          ast_mutex_destroy(&cnf->playlock);
01662          ast_mutex_destroy(&cnf->listenlock);
01663          ast_mutex_destroy(&cnf->recordthreadlock);
01664          ast_mutex_destroy(&cnf->announcethreadlock);
01665          ast_free(cnf);
01666          cnf = NULL;
01667          goto cnfout;
01668       }
01669    }
01670 
01671    cnf->dahdiconf = dahdic.confno;
01672 
01673    /* Setup a new channel for playback of audio files */
01674    cnf->chan = ast_request("DAHDI", cap_slin, NULL, chan, "pseudo", NULL);
01675    if (cnf->chan) {
01676       ast_set_read_format(cnf->chan, ast_format_slin);
01677       ast_set_write_format(cnf->chan, ast_format_slin);
01678       dahdic.chan = 0;
01679       dahdic.confno = cnf->dahdiconf;
01680       dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
01681       if (ioctl(ast_channel_fd(cnf->chan, 0), DAHDI_SETCONF, &dahdic)) {
01682          if (test) {
01683             ast_test_status_update(test, "Error setting conference on pseudo channel\n");
01684          }
01685          ast_log(LOG_WARNING, "Error setting conference\n");
01686          if (cnf->chan)
01687             ast_hangup(cnf->chan);
01688          else
01689             close(cnf->fd);
01690          ao2_ref(cnf->usercontainer, -1);
01691          ast_mutex_destroy(&cnf->playlock);
01692          ast_mutex_destroy(&cnf->listenlock);
01693          ast_mutex_destroy(&cnf->recordthreadlock);
01694          ast_mutex_destroy(&cnf->announcethreadlock);
01695          ast_free(cnf);
01696          cnf = NULL;
01697          goto cnfout;
01698       }
01699    }
01700 
01701    /* Fill the conference struct */
01702    cnf->start = time(NULL);
01703    cnf->maxusers = 0x7fffffff;
01704    cnf->isdynamic = dynamic ? 1 : 0;
01705    ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
01706    AST_LIST_INSERT_HEAD(&confs, cnf, list);
01707 
01708    /* Reserve conference number in map */
01709    if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
01710       conf_map[confno_int] = 1;
01711    
01712 cnfout:
01713    ao2_cleanup(cap_slin);
01714    if (cnf)
01715       ast_atomic_fetchadd_int(&cnf->refcount, refcount);
01716 
01717    AST_LIST_UNLOCK(&confs);
01718 
01719    return cnf;
01720 }
01721 
01722 static char *complete_confno(const char *word, int state)
01723 {
01724    struct ast_conference *cnf;
01725    char *ret = NULL;
01726    int which = 0;
01727    int len = strlen(word);
01728 
01729    AST_LIST_LOCK(&confs);
01730    AST_LIST_TRAVERSE(&confs, cnf, list) {
01731       if (!strncmp(word, cnf->confno, len) && ++which > state) {
01732          /* dup before releasing the lock */
01733          ret = ast_strdup(cnf->confno);
01734          break;
01735       }
01736    }
01737    AST_LIST_UNLOCK(&confs);
01738    return ret;
01739 }
01740 
01741 static char *complete_userno(struct ast_conference *cnf, const char *word, int state)
01742 {
01743    char usrno[50];
01744    struct ao2_iterator iter;
01745    struct ast_conf_user *usr;
01746    char *ret = NULL;
01747    int which = 0;
01748    int len = strlen(word);
01749 
01750    iter = ao2_iterator_init(cnf->usercontainer, 0);
01751    for (; (usr = ao2_iterator_next(&iter)); ao2_ref(usr, -1)) {
01752       snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
01753       if (!strncmp(word, usrno, len) && ++which > state) {
01754          ao2_ref(usr, -1);
01755          ret = ast_strdup(usrno);
01756          break;
01757       }
01758    }
01759    ao2_iterator_destroy(&iter);
01760    return ret;
01761 }
01762 
01763 static char *complete_meetmecmd_mute_kick(const char *line, const char *word, int pos, int state)
01764 {
01765    if (pos == 2) {
01766       return complete_confno(word, state);
01767    }
01768    if (pos == 3) {
01769       int len = strlen(word);
01770       char *ret = NULL;
01771       char *saved = NULL;
01772       char *myline;
01773       char *confno;
01774       struct ast_conference *cnf;
01775 
01776       if (!strncasecmp(word, "all", len)) {
01777          if (state == 0) {
01778             return ast_strdup("all");
01779          }
01780          --state;
01781       }
01782 
01783       /* Extract the confno from the command line. */
01784       myline = ast_strdupa(line);
01785       strtok_r(myline, " ", &saved);
01786       strtok_r(NULL, " ", &saved);
01787       confno = strtok_r(NULL, " ", &saved);
01788 
01789       AST_LIST_LOCK(&confs);
01790       AST_LIST_TRAVERSE(&confs, cnf, list) {
01791          if (!strcmp(confno, cnf->confno)) {
01792             ret = complete_userno(cnf, word, state);
01793             break;
01794          }
01795       }
01796       AST_LIST_UNLOCK(&confs);
01797 
01798       return ret;
01799    }
01800    return NULL;
01801 }
01802 
01803 static char *complete_meetmecmd_lock(const char *word, int pos, int state)
01804 {
01805    if (pos == 2) {
01806       return complete_confno(word, state);
01807    }
01808    return NULL;
01809 }
01810 
01811 static char *complete_meetmecmd_list(const char *line, const char *word, int pos, int state)
01812 {
01813    int len;
01814 
01815    if (pos == 2) {
01816       len = strlen(word);
01817       if (!strncasecmp(word, STR_CONCISE, len)) {
01818          if (state == 0) {
01819             return ast_strdup(STR_CONCISE);
01820          }
01821          --state;
01822       }
01823 
01824       return complete_confno(word, state);
01825    }
01826    if (pos == 3 && state == 0) {
01827       char *saved = NULL;
01828       char *myline;
01829       char *confno;
01830 
01831       /* Extract the confno from the command line. */
01832       myline = ast_strdupa(line);
01833       strtok_r(myline, " ", &saved);
01834       strtok_r(NULL, " ", &saved);
01835       confno = strtok_r(NULL, " ", &saved);
01836 
01837       if (!strcasecmp(confno, STR_CONCISE)) {
01838          /* There is nothing valid in this position now. */
01839          return NULL;
01840       }
01841 
01842       len = strlen(word);
01843       if (!strncasecmp(word, STR_CONCISE, len)) {
01844          return ast_strdup(STR_CONCISE);
01845       }
01846    }
01847    return NULL;
01848 }
01849 
01850 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01851 {
01852    /* Process the command */
01853    struct ast_conf_user *user;
01854    struct ast_conference *cnf;
01855    int hr, min, sec;
01856    int total = 0;
01857    time_t now;
01858 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s  %-8s  %-6s\n"
01859 #define MC_DATA_FORMAT "%-12.12s   %4.4d        %4.4s       %02d:%02d:%02d  %-8s  %-6s\n"
01860 
01861    switch (cmd) {
01862    case CLI_INIT:
01863       e->command = "meetme list";
01864       e->usage =
01865          "Usage: meetme list [<confno>] [" STR_CONCISE "]\n"
01866          "       List all conferences or a specific conference.\n";
01867       return NULL;
01868    case CLI_GENERATE:
01869       return complete_meetmecmd_list(a->line, a->word, a->pos, a->n);
01870    }
01871 
01872    if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], STR_CONCISE))) {
01873       /* List all the conferences */
01874       int concise = (a->argc == 3);
01875       struct ast_str *marked_users;
01876 
01877       if (!(marked_users = ast_str_create(30))) {
01878          return CLI_FAILURE;
01879       }
01880 
01881       now = time(NULL);
01882       AST_LIST_LOCK(&confs);
01883       if (AST_LIST_EMPTY(&confs)) {
01884          if (!concise) {
01885             ast_cli(a->fd, "No active MeetMe conferences.\n");
01886          }
01887          AST_LIST_UNLOCK(&confs);
01888          ast_free(marked_users);
01889          return CLI_SUCCESS;
01890       }
01891       if (!concise) {
01892          ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
01893       }
01894       AST_LIST_TRAVERSE(&confs, cnf, list) {
01895          hr = (now - cnf->start) / 3600;
01896          min = ((now - cnf->start) % 3600) / 60;
01897          sec = (now - cnf->start) % 60;
01898          if (!concise) {
01899             if (cnf->markedusers == 0) {
01900                ast_str_set(&marked_users, 0, "N/A ");
01901             } else {
01902                ast_str_set(&marked_users, 0, "%4.4d", cnf->markedusers);
01903             }
01904             ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users,
01905                ast_str_buffer(marked_users), hr, min, sec,
01906                cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
01907          } else {
01908             ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
01909                cnf->confno,
01910                cnf->users,
01911                cnf->markedusers,
01912                hr, min, sec,
01913                cnf->isdynamic,
01914                cnf->locked);
01915          }
01916 
01917          total += cnf->users;
01918       }
01919       AST_LIST_UNLOCK(&confs);
01920       if (!concise) {
01921          ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
01922       }
01923       ast_free(marked_users);
01924       return CLI_SUCCESS;
01925    }
01926    if (a->argc == 3 || (a->argc == 4 && !strcasecmp(a->argv[3], STR_CONCISE))) {
01927       struct ao2_iterator user_iter;
01928       int concise = (a->argc == 4);
01929 
01930       /* List all the users in a conference */
01931       if (AST_LIST_EMPTY(&confs)) {
01932          if (!concise) {
01933             ast_cli(a->fd, "No active MeetMe conferences.\n");
01934          }
01935          return CLI_SUCCESS;
01936       }
01937       /* Find the right conference */
01938       AST_LIST_LOCK(&confs);
01939       AST_LIST_TRAVERSE(&confs, cnf, list) {
01940          if (strcmp(cnf->confno, a->argv[2]) == 0) {
01941             break;
01942          }
01943       }
01944       if (!cnf) {
01945          if (!concise)
01946             ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
01947          AST_LIST_UNLOCK(&confs);
01948          return CLI_SUCCESS;
01949       }
01950       /* Show all the users */
01951       time(&now);
01952       user_iter = ao2_iterator_init(cnf->usercontainer, 0);
01953       while((user = ao2_iterator_next(&user_iter))) {
01954          hr = (now - user->jointime) / 3600;
01955          min = ((now - user->jointime) % 3600) / 60;
01956          sec = (now - user->jointime) % 60;
01957          if (!concise) {
01958             ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
01959                user->user_no,
01960                S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
01961                S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
01962                ast_channel_name(user->chan),
01963                ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
01964                ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
01965                user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
01966                user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
01967                istalking(user->talking), hr, min, sec); 
01968          } else {
01969             ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
01970                user->user_no,
01971                S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, ""),
01972                S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, ""),
01973                ast_channel_name(user->chan),
01974                ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
01975                ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
01976                user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
01977                user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
01978                user->talking, hr, min, sec);
01979          }
01980          ao2_ref(user, -1);
01981       }
01982       ao2_iterator_destroy(&user_iter);
01983       if (!concise) {
01984          ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
01985       }
01986       AST_LIST_UNLOCK(&confs);
01987       return CLI_SUCCESS;
01988    }
01989    return CLI_SHOWUSAGE;
01990 }
01991 
01992 
01993 static char *meetme_cmd_helper(struct ast_cli_args *a)
01994 {
01995    /* Process the command */
01996    struct ast_str *cmdline;
01997 
01998    /* Max confno length */
01999    if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
02000       return CLI_FAILURE;
02001    }
02002 
02003    ast_str_set(&cmdline, 0, "%s", a->argv[2]);  /* Argv 2: conference number */
02004    if (strcasestr(a->argv[1], "lock")) {
02005       if (strcasecmp(a->argv[1], "lock") == 0) {
02006          /* Lock */
02007          ast_str_append(&cmdline, 0, ",L");
02008       } else {
02009          /* Unlock */
02010          ast_str_append(&cmdline, 0, ",l");
02011       }
02012    } else if (strcasestr(a->argv[1], "mute")) { 
02013       if (strcasecmp(a->argv[1], "mute") == 0) {
02014          /* Mute */
02015          if (strcasecmp(a->argv[3], "all") == 0) {
02016             ast_str_append(&cmdline, 0, ",N");
02017          } else {
02018             ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);  
02019          }
02020       } else {
02021          /* Unmute */
02022          if (strcasecmp(a->argv[3], "all") == 0) {
02023             ast_str_append(&cmdline, 0, ",n");
02024          } else {
02025             ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
02026          }
02027       }
02028    } else if (strcasecmp(a->argv[1], "kick") == 0) {
02029       if (strcasecmp(a->argv[3], "all") == 0) {
02030          /* Kick all */
02031          ast_str_append(&cmdline, 0, ",K");
02032       } else {
02033          /* Kick a single user */
02034          ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
02035       }
02036    } else {
02037       /*
02038        * Should never get here because it is already filtered by the
02039        * callers.
02040        */
02041       ast_free(cmdline);
02042       return CLI_SHOWUSAGE;
02043    }
02044 
02045    ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
02046 
02047    admin_exec(NULL, ast_str_buffer(cmdline));
02048    ast_free(cmdline);
02049 
02050    return CLI_SUCCESS;
02051 }
02052 
02053 static char *meetme_lock_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02054 {
02055    switch (cmd) {
02056    case CLI_INIT:
02057       e->command = "meetme {lock|unlock}";
02058       e->usage =
02059          "Usage: meetme lock|unlock <confno>\n"
02060          "       Lock or unlock a conference to new users.\n";
02061       return NULL;
02062    case CLI_GENERATE:
02063       return complete_meetmecmd_lock(a->word, a->pos, a->n);
02064    }
02065 
02066    if (a->argc != 3) {
02067       return CLI_SHOWUSAGE;
02068    }
02069 
02070    return meetme_cmd_helper(a);
02071 }
02072 
02073 static char *meetme_kick_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02074 {
02075    switch (cmd) {
02076    case CLI_INIT:
02077       e->command = "meetme kick";
02078       e->usage =
02079          "Usage: meetme kick <confno> all|<userno>\n"
02080          "       Kick a conference or a user in a conference.\n";
02081       return NULL;
02082    case CLI_GENERATE:
02083       return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
02084    }
02085 
02086    if (a->argc != 4) {
02087       return CLI_SHOWUSAGE;
02088    }
02089 
02090    return meetme_cmd_helper(a);
02091 }
02092 
02093 static char *meetme_mute_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02094 {
02095    switch (cmd) {
02096    case CLI_INIT:
02097       e->command = "meetme {mute|unmute}";
02098       e->usage =
02099          "Usage: meetme mute|unmute <confno> all|<userno>\n"
02100          "       Mute or unmute a conference or a user in a conference.\n";
02101       return NULL;
02102    case CLI_GENERATE:
02103       return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
02104    }
02105 
02106    if (a->argc != 4) {
02107       return CLI_SHOWUSAGE;
02108    }
02109 
02110    return meetme_cmd_helper(a);
02111 }
02112 
02113 static const char *sla_hold_str(unsigned int hold_access)
02114 {
02115    const char *hold = "Unknown";
02116 
02117    switch (hold_access) {
02118    case SLA_HOLD_OPEN:
02119       hold = "Open";
02120       break;
02121    case SLA_HOLD_PRIVATE:
02122       hold = "Private";
02123    default:
02124       break;
02125    }
02126 
02127    return hold;
02128 }
02129 
02130 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02131 {
02132    struct ao2_iterator i;
02133    struct sla_trunk *trunk;
02134 
02135    switch (cmd) {
02136    case CLI_INIT:
02137       e->command = "sla show trunks";
02138       e->usage =
02139          "Usage: sla show trunks\n"
02140          "       This will list all trunks defined in sla.conf\n";
02141       return NULL;
02142    case CLI_GENERATE:
02143       return NULL;
02144    }
02145 
02146    ast_cli(a->fd, "\n"
02147                "=============================================================\n"
02148                "=== Configured SLA Trunks ===================================\n"
02149                "=============================================================\n"
02150                "===\n");
02151    i = ao2_iterator_init(sla_trunks, 0);
02152    for (; (trunk = ao2_iterator_next(&i)); ao2_ref(trunk, -1)) {
02153       struct sla_station_ref *station_ref;
02154       char ring_timeout[16] = "(none)";
02155 
02156       ao2_lock(trunk);
02157 
02158       if (trunk->ring_timeout) {
02159          snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
02160       }
02161 
02162       ast_cli(a->fd, "=== ---------------------------------------------------------\n"
02163                   "=== Trunk Name:       %s\n"
02164                   "=== ==> Device:       %s\n"
02165                   "=== ==> AutoContext:  %s\n"
02166                   "=== ==> RingTimeout:  %s\n"
02167                   "=== ==> BargeAllowed: %s\n"
02168                   "=== ==> HoldAccess:   %s\n"
02169                   "=== ==> Stations ...\n",
02170                   trunk->name, trunk->device, 
02171                   S_OR(trunk->autocontext, "(none)"), 
02172                   ring_timeout,
02173                   trunk->barge_disabled ? "No" : "Yes",
02174                   sla_hold_str(trunk->hold_access));
02175 
02176       AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
02177          ast_cli(a->fd, "===    ==> Station name: %s\n", station_ref->station->name);
02178       }
02179 
02180       ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
02181 
02182       ao2_unlock(trunk);
02183    }
02184    ao2_iterator_destroy(&i);
02185    ast_cli(a->fd, "=============================================================\n\n");
02186 
02187    return CLI_SUCCESS;
02188 }
02189 
02190 static const char *trunkstate2str(enum sla_trunk_state state)
02191 {
02192 #define S(e) case e: return # e;
02193    switch (state) {
02194    S(SLA_TRUNK_STATE_IDLE)
02195    S(SLA_TRUNK_STATE_RINGING)
02196    S(SLA_TRUNK_STATE_UP)
02197    S(SLA_TRUNK_STATE_ONHOLD)
02198    S(SLA_TRUNK_STATE_ONHOLD_BYME)
02199    }
02200    return "Uknown State";
02201 #undef S
02202 }
02203 
02204 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02205 {
02206    struct ao2_iterator i;
02207    struct sla_station *station;
02208 
02209    switch (cmd) {
02210    case CLI_INIT:
02211       e->command = "sla show stations";
02212       e->usage =
02213          "Usage: sla show stations\n"
02214          "       This will list all stations defined in sla.conf\n";
02215       return NULL;
02216    case CLI_GENERATE:
02217       return NULL;
02218    }
02219 
02220    ast_cli(a->fd, "\n" 
02221                "=============================================================\n"
02222                "=== Configured SLA Stations =================================\n"
02223                "=============================================================\n"
02224                "===\n");
02225    i = ao2_iterator_init(sla_stations, 0);
02226    for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
02227       struct sla_trunk_ref *trunk_ref;
02228       char ring_timeout[16] = "(none)";
02229       char ring_delay[16] = "(none)";
02230 
02231       ao2_lock(station);
02232 
02233       if (station->ring_timeout) {
02234          snprintf(ring_timeout, sizeof(ring_timeout), 
02235             "%u", station->ring_timeout);
02236       }
02237       if (station->ring_delay) {
02238          snprintf(ring_delay, sizeof(ring_delay), 
02239             "%u", station->ring_delay);
02240       }
02241       ast_cli(a->fd, "=== ---------------------------------------------------------\n"
02242                   "=== Station Name:    %s\n"
02243                   "=== ==> Device:      %s\n"
02244                   "=== ==> AutoContext: %s\n"
02245                   "=== ==> RingTimeout: %s\n"
02246                   "=== ==> RingDelay:   %s\n"
02247                   "=== ==> HoldAccess:  %s\n"
02248                   "=== ==> Trunks ...\n",
02249                   station->name, station->device,
02250                   S_OR(station->autocontext, "(none)"), 
02251                   ring_timeout, ring_delay,
02252                   sla_hold_str(station->hold_access));
02253       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
02254          if (trunk_ref->ring_timeout) {
02255             snprintf(ring_timeout, sizeof(ring_timeout),
02256                "%u", trunk_ref->ring_timeout);
02257          } else
02258             strcpy(ring_timeout, "(none)");
02259          if (trunk_ref->ring_delay) {
02260             snprintf(ring_delay, sizeof(ring_delay),
02261                "%u", trunk_ref->ring_delay);
02262          } else
02263             strcpy(ring_delay, "(none)");
02264             ast_cli(a->fd, "===    ==> Trunk Name: %s\n"
02265                      "===       ==> State:       %s\n"
02266                      "===       ==> RingTimeout: %s\n"
02267                      "===       ==> RingDelay:   %s\n",
02268                      trunk_ref->trunk->name,
02269                      trunkstate2str(trunk_ref->state),
02270                      ring_timeout, ring_delay);
02271       }
02272       ast_cli(a->fd, "=== ---------------------------------------------------------\n"
02273                   "===\n");
02274 
02275       ao2_unlock(station);
02276    }
02277    ao2_iterator_destroy(&i);
02278    ast_cli(a->fd, "============================================================\n"
02279                "\n");
02280 
02281    return CLI_SUCCESS;
02282 }
02283 
02284 static struct ast_cli_entry cli_meetme[] = {
02285    AST_CLI_DEFINE(meetme_kick_cmd, "Kick a conference or a user in a conference."),
02286    AST_CLI_DEFINE(meetme_show_cmd, "List all conferences or a specific conference."),
02287    AST_CLI_DEFINE(meetme_lock_cmd, "Lock or unlock a conference to new users."),
02288    AST_CLI_DEFINE(meetme_mute_cmd, "Mute or unmute a conference or a user in a conference."),
02289    AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
02290    AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
02291 };
02292 
02293 static void conf_flush(int fd, struct ast_channel *chan)
02294 {
02295    int x;
02296 
02297    /* read any frames that may be waiting on the channel
02298       and throw them away
02299    */
02300    if (chan) {
02301       struct ast_frame *f;
02302 
02303       /* when no frames are available, this will wait
02304          for 1 millisecond maximum
02305       */
02306       while (ast_waitfor(chan, 1) > 0) {
02307          f = ast_read(chan);
02308          if (f)
02309             ast_frfree(f);
02310          else /* channel was hung up or something else happened */
02311             break;
02312       }
02313    }
02314 
02315    /* flush any data sitting in the pseudo channel */
02316    x = DAHDI_FLUSH_ALL;
02317    if (ioctl(fd, DAHDI_FLUSH, &x))
02318       ast_log(LOG_WARNING, "Error flushing channel\n");
02319 
02320 }
02321 
02322 /*! \brief Remove the conference from the list and free it.
02323 
02324    We assume that this was called while holding conflock. */
02325 static int conf_free(struct ast_conference *conf)
02326 {
02327    int x;
02328    struct announce_listitem *item;
02329 
02330    AST_LIST_REMOVE(&confs, conf, list);
02331 
02332    meetme_stasis_generate_msg(conf, NULL, NULL, meetme_end_type(), NULL);
02333 
02334    if (conf->recording == MEETME_RECORD_ACTIVE) {
02335       conf->recording = MEETME_RECORD_TERMINATE;
02336       AST_LIST_UNLOCK(&confs);
02337       while (1) {
02338          usleep(1);
02339          AST_LIST_LOCK(&confs);
02340          if (conf->recording == MEETME_RECORD_OFF)
02341             break;
02342          AST_LIST_UNLOCK(&confs);
02343       }
02344    }
02345 
02346    for (x = 0; x < AST_FRAME_BITS; x++) {
02347       if (conf->transframe[x])
02348          ast_frfree(conf->transframe[x]);
02349       if (conf->transpath[x])
02350          ast_translator_free_path(conf->transpath[x]);
02351    }
02352    if (conf->announcethread != AST_PTHREADT_NULL) {
02353       ast_mutex_lock(&conf->announcelistlock);
02354       conf->announcethread_stop = 1;
02355       ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
02356       ast_cond_signal(&conf->announcelist_addition);
02357       ast_mutex_unlock(&conf->announcelistlock);
02358       pthread_join(conf->announcethread, NULL);
02359    
02360       while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
02361          /* If it's a voicemail greeting file we don't want to remove it */
02362          if (!item->vmrec){
02363             ast_filedelete(item->namerecloc, NULL);
02364          }
02365          ao2_ref(item, -1);
02366       }
02367       ast_mutex_destroy(&conf->announcelistlock);
02368    }
02369 
02370    if (conf->origframe)
02371       ast_frfree(conf->origframe);
02372    ast_hangup(conf->lchan);
02373    ast_hangup(conf->chan);
02374    if (conf->fd >= 0)
02375       close(conf->fd);
02376    if (conf->recordingfilename) {
02377       ast_free(conf->recordingfilename);
02378    }
02379    if (conf->usercontainer) {
02380       ao2_ref(conf->usercontainer, -1);
02381    }
02382    if (conf->recordingformat) {
02383       ast_free(conf->recordingformat);
02384    }
02385    ast_mutex_destroy(&conf->playlock);
02386    ast_mutex_destroy(&conf->listenlock);
02387    ast_mutex_destroy(&conf->recordthreadlock);
02388    ast_mutex_destroy(&conf->announcethreadlock);
02389    ast_free(conf);
02390 
02391    return 0;
02392 }
02393 
02394 static void conf_queue_dtmf(const struct ast_conference *conf,
02395    const struct ast_conf_user *sender, struct ast_frame *f)
02396 {
02397    struct ast_conf_user *user;
02398    struct ao2_iterator user_iter;
02399 
02400    user_iter = ao2_iterator_init(conf->usercontainer, 0);
02401    while ((user = ao2_iterator_next(&user_iter))) {
02402       if (user == sender) {
02403          ao2_ref(user, -1);
02404          continue;
02405       }
02406       if (ast_write(user->chan, f) < 0)
02407          ast_log(LOG_WARNING, "Error writing frame to channel %s\n", ast_channel_name(user->chan));
02408       ao2_ref(user, -1);
02409    }
02410    ao2_iterator_destroy(&user_iter);
02411 }
02412 
02413 static void sla_queue_event_full(enum sla_event_type type, 
02414    struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
02415 {
02416    struct sla_event *event;
02417 
02418    if (sla.thread == AST_PTHREADT_NULL) {
02419       ao2_ref(station, -1);
02420       ao2_ref(trunk_ref, -1);
02421       return;
02422    }
02423 
02424    if (!(event = ast_calloc(1, sizeof(*event)))) {
02425       ao2_ref(station, -1);
02426       ao2_ref(trunk_ref, -1);
02427       return;
02428    }
02429 
02430    event->type = type;
02431    event->trunk_ref = trunk_ref;
02432    event->station = station;
02433 
02434    if (!lock) {
02435       AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
02436       return;
02437    }
02438 
02439    ast_mutex_lock(&sla.lock);
02440    AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
02441    ast_cond_signal(&sla.cond);
02442    ast_mutex_unlock(&sla.lock);
02443 }
02444 
02445 static void sla_queue_event_nolock(enum sla_event_type type)
02446 {
02447    sla_queue_event_full(type, NULL, NULL, 0);
02448 }
02449 
02450 static void sla_queue_event(enum sla_event_type type)
02451 {
02452    sla_queue_event_full(type, NULL, NULL, 1);
02453 }
02454 
02455 /*! \brief Queue a SLA event from the conference */
02456 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
02457    struct ast_conference *conf)
02458 {
02459    struct sla_station *station;
02460    struct sla_trunk_ref *trunk_ref = NULL;
02461    char *trunk_name;
02462    struct ao2_iterator i;
02463 
02464    trunk_name = ast_strdupa(conf->confno);
02465    strsep(&trunk_name, "_");
02466    if (ast_strlen_zero(trunk_name)) {
02467       ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
02468       return;
02469    }
02470 
02471    i = ao2_iterator_init(sla_stations, 0);
02472    while ((station = ao2_iterator_next(&i))) {
02473       ao2_lock(station);
02474       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
02475          if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name)) {
02476             ao2_ref(trunk_ref, 1);
02477             break;
02478          }
02479       }
02480       ao2_unlock(station);
02481       if (trunk_ref) {
02482          /* station reference given to sla_queue_event_full() */
02483          break;
02484       }
02485       ao2_ref(station, -1);
02486    }
02487    ao2_iterator_destroy(&i);
02488 
02489    if (!trunk_ref) {
02490       ast_debug(1, "Trunk not found for event!\n");
02491       return;
02492    }
02493 
02494    sla_queue_event_full(type, trunk_ref, station, 1);
02495 }
02496 
02497 /*! \brief Decrement reference counts, as incremented by find_conf() */
02498 static int dispose_conf(struct ast_conference *conf)
02499 {
02500    int res = 0;
02501    int confno_int = 0;
02502 
02503    AST_LIST_LOCK(&confs);
02504    if (ast_atomic_dec_and_test(&conf->refcount)) {
02505       /* Take the conference room number out of an inuse state */
02506       if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
02507          conf_map[confno_int] = 0;
02508       }
02509       conf_free(conf);
02510       res = 1;
02511    }
02512    AST_LIST_UNLOCK(&confs);
02513 
02514    return res;
02515 }
02516 
02517 static int rt_extend_conf(const char *confno)
02518 {
02519    char currenttime[32];
02520    char endtime[32];
02521    struct timeval now;
02522    struct ast_tm tm;
02523    struct ast_variable *var, *orig_var;
02524    char bookid[51];
02525 
02526    if (!extendby) {
02527       return 0;
02528    }
02529 
02530    now = ast_tvnow();
02531 
02532    ast_localtime(&now, &tm, NULL);
02533    ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
02534 
02535    var = ast_load_realtime("meetme", "confno",
02536       confno, "startTime<= ", currenttime,
02537       "endtime>= ", currenttime, NULL);
02538 
02539    orig_var = var;
02540 
02541    /* Identify the specific RealTime conference */
02542    while (var) {
02543       if (!strcasecmp(var->name, "bookid")) {
02544          ast_copy_string(bookid, var->value, sizeof(bookid));
02545       }
02546       if (!strcasecmp(var->name, "endtime")) {
02547          ast_copy_string(endtime, var->value, sizeof(endtime));
02548       }
02549 
02550       var = var->next;
02551    }
02552    ast_variables_destroy(orig_var);
02553 
02554    ast_strptime(endtime, DATE_FORMAT, &tm);
02555    now = ast_mktime(&tm, NULL);
02556 
02557    now.tv_sec += extendby;
02558 
02559    ast_localtime(&now, &tm, NULL);
02560    ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
02561    strcat(currenttime, "0"); /* Seconds needs to be 00 */
02562 
02563    var = ast_load_realtime("meetme", "confno",
02564       confno, "startTime<= ", currenttime,
02565       "endtime>= ", currenttime, NULL);
02566 
02567    /* If there is no conflict with extending the conference, update the DB */
02568    if (!var) {
02569       ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
02570       ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
02571       return 0;
02572 
02573    }
02574 
02575    ast_variables_destroy(var);
02576    return -1;
02577 }
02578 
02579 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
02580 {
02581    char *original_moh;
02582 
02583    ast_channel_lock(chan);
02584    original_moh = ast_strdupa(ast_channel_musicclass(chan));
02585    ast_channel_musicclass_set(chan, musicclass);
02586    ast_channel_unlock(chan);
02587 
02588    ast_moh_start(chan, original_moh, NULL);
02589 
02590    ast_channel_lock(chan);
02591    ast_channel_musicclass_set(chan, original_moh);
02592    ast_channel_unlock(chan);
02593 }
02594 
02595 static const char *get_announce_filename(enum announcetypes type)
02596 {
02597    switch (type) {
02598    case CONF_HASLEFT:
02599       return "conf-hasleft";
02600       break;
02601    case CONF_HASJOIN:
02602       return "conf-hasjoin";
02603       break;
02604    default:
02605       return "";
02606    }
02607 }
02608 
02609 static void *announce_thread(void *data)
02610 {
02611    struct announce_listitem *current;
02612    struct ast_conference *conf = data;
02613    int res;
02614    char filename[PATH_MAX] = "";
02615    AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
02616    AST_LIST_HEAD_INIT_NOLOCK(&local_list);
02617 
02618    while (!conf->announcethread_stop) {
02619       ast_mutex_lock(&conf->announcelistlock);
02620       if (conf->announcethread_stop) {
02621          ast_mutex_unlock(&conf->announcelistlock);
02622          break;
02623       }
02624       if (AST_LIST_EMPTY(&conf->announcelist))
02625          ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
02626 
02627       AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
02628       AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
02629 
02630       ast_mutex_unlock(&conf->announcelistlock);
02631       if (conf->announcethread_stop) {
02632          break;
02633       }
02634 
02635       for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
02636          ast_debug(1, "About to play %s\n", current->namerecloc);
02637          if (!ast_fileexists(current->namerecloc, NULL, NULL))
02638             continue;
02639          if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
02640             if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
02641                res = ast_waitstream(current->confchan, "");
02642             if (!res) {
02643                ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
02644                if (!ast_streamfile(current->confchan, filename, current->language))
02645                   ast_waitstream(current->confchan, "");
02646             }
02647          }
02648          if (current->announcetype == CONF_HASLEFT && current->announcetype && !current->vmrec) {
02649             /* only remove it if it isn't a VM recording file */
02650             ast_filedelete(current->namerecloc, NULL);
02651          }
02652       }
02653    }
02654 
02655    /* thread marked to stop, clean up */
02656    while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
02657       /* only delete if it's a vm rec */
02658       if (!current->vmrec) {
02659          ast_filedelete(current->namerecloc, NULL);
02660       }
02661       ao2_ref(current, -1);
02662    }
02663    return NULL;
02664 }
02665 
02666 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
02667 {
02668    if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
02669       return 1;
02670    }
02671 
02672    return (ast_channel_state(chan) == AST_STATE_UP);
02673 }
02674 
02675 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
02676 {
02677    RAII_VAR(struct ast_json *, status_blob, status_to_json(talking), ast_json_unref);
02678    meetme_stasis_generate_msg(conf, chan, user, meetme_talking_type(), status_blob);
02679 }
02680 
02681 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
02682 {
02683    int last_talking = user->talking;
02684    if (last_talking == talking)
02685       return;
02686 
02687    user->talking = talking;
02688 
02689    if (monitor) {
02690       /* Check if talking state changed. Take care of -1 which means unmonitored */
02691       int was_talking = (last_talking > 0);
02692       int now_talking = (talking > 0);
02693       if (was_talking != now_talking) {
02694          send_talking_event(chan, conf, user, now_talking);
02695       }
02696    }
02697 }
02698 
02699 static int user_set_hangup_cb(void *obj, void *check_admin_arg, int flags)
02700 {
02701    struct ast_conf_user *user = obj;
02702    /* actual pointer contents of check_admin_arg is irrelevant */
02703 
02704    if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
02705       user->adminflags |= ADMINFLAG_HANGUP;
02706    }
02707    return 0;
02708 }
02709 
02710 static int user_set_kickme_cb(void *obj, void *check_admin_arg, int flags)
02711 {
02712    struct ast_conf_user *user = obj;
02713    /* actual pointer contents of check_admin_arg is irrelevant */
02714 
02715    if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
02716       user->adminflags |= ADMINFLAG_KICKME;
02717    }
02718    return 0;
02719 }
02720 
02721 static int user_set_unmuted_cb(void *obj, void *check_admin_arg, int flags)
02722 {
02723    struct ast_conf_user *user = obj;
02724    /* actual pointer contents of check_admin_arg is irrelevant */
02725 
02726    if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
02727       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
02728    }
02729    return 0;
02730 }
02731 
02732 static int user_set_muted_cb(void *obj, void *check_admin_arg, int flags)
02733 {
02734    struct ast_conf_user *user = obj;
02735    /* actual pointer contents of check_admin_arg is irrelevant */
02736 
02737    if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
02738       user->adminflags |= ADMINFLAG_MUTED;
02739    }
02740    return 0;
02741 }
02742 
02743 enum menu_modes {
02744    MENU_DISABLED = 0,
02745    MENU_NORMAL,
02746    MENU_ADMIN,
02747    MENU_ADMIN_EXTENDED,
02748 };
02749 
02750 /*! \internal
02751  * \brief Processes menu options for the standard menu (accessible through the 's' option for app_meetme)
02752  *
02753  * \param menu_mode a pointer to the currently active menu_mode.
02754  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
02755  * \param conf the active conference for which the user has called the menu from.
02756  * \param confflags flags used by conf for various options
02757  * \param chan ast_channel belonging to the user who called the menu
02758  * \param user which meetme conference user invoked the menu
02759  */
02760 static void meetme_menu_normal(enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user)
02761 {
02762    switch (*dtmf) {
02763    case '1': /* Un/Mute */
02764       *menu_mode = MENU_DISABLED;
02765 
02766       /* user can only toggle the self-muted state */
02767       user->adminflags ^= ADMINFLAG_SELFMUTED;
02768 
02769       /* they can't override the admin mute state */
02770       if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
02771          if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
02772             ast_waitstream(chan, "");
02773          }
02774       } else {
02775          if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
02776             ast_waitstream(chan, "");
02777          }
02778       }
02779       break;
02780 
02781    case '2':
02782       *menu_mode = MENU_DISABLED;
02783       if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
02784          user->adminflags |= ADMINFLAG_T_REQUEST;
02785       }
02786 
02787       if (user->adminflags & ADMINFLAG_T_REQUEST) {
02788          if (!ast_streamfile(chan, "beep", ast_channel_language(chan))) {
02789             ast_waitstream(chan, "");
02790          }
02791       }
02792       break;
02793 
02794    case '4':
02795       tweak_listen_volume(user, VOL_DOWN);
02796       break;
02797    case '5':
02798       /* Extend RT conference */
02799       if (rt_schedule) {
02800          rt_extend_conf(conf->confno);
02801       }
02802       *menu_mode = MENU_DISABLED;
02803       break;
02804 
02805    case '6':
02806       tweak_listen_volume(user, VOL_UP);
02807       break;
02808 
02809    case '7':
02810       tweak_talk_volume(user, VOL_DOWN);
02811       break;
02812 
02813    case '8':
02814       *menu_mode = MENU_DISABLED;
02815       break;
02816 
02817    case '9':
02818       tweak_talk_volume(user, VOL_UP);
02819       break;
02820 
02821    default:
02822       *menu_mode = MENU_DISABLED;
02823       if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02824          ast_waitstream(chan, "");
02825       }
02826       break;
02827    }
02828 }
02829 
02830 /*! \internal
02831  * \brief Processes menu options for the adminstrator menu (accessible through the 's' option for app_meetme)
02832  *
02833  * \param menu_mode a pointer to the currently active menu_mode.
02834  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
02835  * \param conf the active conference for which the user has called the menu from.
02836  * \param confflags flags used by conf for various options
02837  * \param chan ast_channel belonging to the user who called the menu
02838  * \param user which meetme conference user invoked the menu
02839  */
02840 static void meetme_menu_admin(enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user)
02841 {
02842    switch(*dtmf) {
02843    case '1': /* Un/Mute */
02844       *menu_mode = MENU_DISABLED;
02845       /* for admin, change both admin and use flags */
02846       if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
02847          user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
02848       } else {
02849          user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
02850       }
02851 
02852       if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
02853          if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
02854             ast_waitstream(chan, "");
02855          }
02856       } else {
02857          if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
02858             ast_waitstream(chan, "");
02859          }
02860       }
02861       break;
02862 
02863    case '2': /* Un/Lock the Conference */
02864       *menu_mode = MENU_DISABLED;
02865       if (conf->locked) {
02866          conf->locked = 0;
02867          if (!ast_streamfile(chan, "conf-unlockednow", ast_channel_language(chan))) {
02868             ast_waitstream(chan, "");
02869          }
02870       } else {
02871          conf->locked = 1;
02872          if (!ast_streamfile(chan, "conf-lockednow", ast_channel_language(chan))) {
02873             ast_waitstream(chan, "");
02874          }
02875       }
02876       break;
02877 
02878    case '3': /* Eject last user */
02879    {
02880       struct ast_conf_user *usr = NULL;
02881       int max_no = 0;
02882       ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
02883       *menu_mode = MENU_DISABLED;
02884       usr = ao2_find(conf->usercontainer, &max_no, 0);
02885       if ((ast_channel_name(usr->chan) == ast_channel_name(chan)) || ast_test_flag64(&usr->userflags, CONFFLAG_ADMIN)) {
02886          if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02887             ast_waitstream(chan, "");
02888          }
02889       } else {
02890          usr->adminflags |= ADMINFLAG_KICKME;
02891       }
02892       ao2_ref(usr, -1);
02893       ast_stopstream(chan);
02894       break;
02895    }
02896 
02897    case '4':
02898       tweak_listen_volume(user, VOL_DOWN);
02899       break;
02900 
02901    case '5':
02902       /* Extend RT conference */
02903       if (rt_schedule) {
02904          if (!rt_extend_conf(conf->confno)) {
02905             if (!ast_streamfile(chan, "conf-extended", ast_channel_language(chan))) {
02906                ast_waitstream(chan, "");
02907             }
02908          } else {
02909             if (!ast_streamfile(chan, "conf-nonextended", ast_channel_language(chan))) {
02910                ast_waitstream(chan, "");
02911             }
02912          }
02913          ast_stopstream(chan);
02914       }
02915       *menu_mode = MENU_DISABLED;
02916       break;
02917 
02918    case '6':
02919       tweak_listen_volume(user, VOL_UP);
02920       break;
02921 
02922    case '7':
02923       tweak_talk_volume(user, VOL_DOWN);
02924       break;
02925 
02926    case '8':
02927       if (!ast_streamfile(chan, "conf-adminmenu-menu8", ast_channel_language(chan))) {
02928          /* If the user provides DTMF while playing the sound, we want to drop right into the extended menu function with new DTMF once we get out of here. */
02929          *dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
02930          ast_stopstream(chan);
02931       }
02932       *menu_mode = MENU_ADMIN_EXTENDED;
02933       break;
02934 
02935    case '9':
02936       tweak_talk_volume(user, VOL_UP);
02937       break;
02938    default:
02939       menu_mode = MENU_DISABLED;
02940       /* Play an error message! */
02941       if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02942          ast_waitstream(chan, "");
02943       }
02944       break;
02945    }
02946 
02947 }
02948 
02949 /*! \internal
02950  * \brief Processes menu options for the extended administrator menu (accessible through option 8 on the administrator menu)
02951  *
02952  * \param menu_mode a pointer to the currently active menu_mode.
02953  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
02954  * \param conf the active conference for which the user has called the menu from.
02955  * \param confflags flags used by conf for various options
02956  * \param chan ast_channel belonging to the user who called the menu
02957  * \param user which meetme conference user invoked the menu
02958  * \param recordingtmp character buffer which may hold the name of the conference recording file
02959  */
02960 static void meetme_menu_admin_extended(enum menu_modes *menu_mode, int *dtmf,
02961    struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan,
02962    struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size,
02963    struct ast_format_cap *cap_slin)
02964 {
02965    int keepplaying;
02966    int playednamerec;
02967    int res;
02968    struct ao2_iterator user_iter;
02969    struct ast_conf_user *usr = NULL;
02970 
02971    switch(*dtmf) {
02972    case '1': /* *81 Roll call */
02973       keepplaying = 1;
02974       playednamerec = 0;
02975       if (conf->users == 1) {
02976          if (keepplaying && !ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
02977             res = ast_waitstream(chan, AST_DIGIT_ANY);
02978             ast_stopstream(chan);
02979             if (res > 0) {
02980                keepplaying = 0;
02981             }
02982          }
02983       } else if (conf->users == 2) {
02984          if (keepplaying && !ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
02985             res = ast_waitstream(chan, AST_DIGIT_ANY);
02986             ast_stopstream(chan);
02987             if (res > 0) {
02988                keepplaying = 0;
02989             }
02990          }
02991       } else {
02992          if (keepplaying && !ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
02993             res = ast_waitstream(chan, AST_DIGIT_ANY);
02994             ast_stopstream(chan);
02995             if (res > 0) {
02996                keepplaying = 0;
02997             }
02998          }
02999          if (keepplaying) {
03000             res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03001             ast_stopstream(chan);
03002             if (res > 0) {
03003                keepplaying = 0;
03004             }
03005          }
03006          if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
03007             res = ast_waitstream(chan, AST_DIGIT_ANY);
03008             ast_stopstream(chan);
03009             if (res > 0) {
03010                keepplaying = 0;
03011             }
03012          }
03013       }
03014       user_iter = ao2_iterator_init(conf->usercontainer, 0);
03015       while((usr = ao2_iterator_next(&user_iter))) {
03016          if (ast_fileexists(usr->namerecloc, NULL, NULL)) {
03017             if (keepplaying && !ast_streamfile(chan, usr->namerecloc, ast_channel_language(chan))) {
03018                res = ast_waitstream(chan, AST_DIGIT_ANY);
03019                ast_stopstream(chan);
03020                if (res > 0) {
03021                   keepplaying = 0;
03022                }
03023             }
03024             playednamerec = 1;
03025          }
03026          ao2_ref(usr, -1);
03027       }
03028       ao2_iterator_destroy(&user_iter);
03029       if (keepplaying && playednamerec && !ast_streamfile(chan, "conf-roll-callcomplete", ast_channel_language(chan))) {
03030          res = ast_waitstream(chan, AST_DIGIT_ANY);
03031          ast_stopstream(chan);
03032          if (res > 0) {
03033             keepplaying = 0;
03034          }
03035       }
03036 
03037       *menu_mode = MENU_DISABLED;
03038       break;
03039 
03040    case '2': /* *82 Eject all non-admins */
03041       if (conf->users == 1) {
03042          if(!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
03043             ast_waitstream(chan, "");
03044          }
03045       } else {
03046          ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_kickme_cb, &conf);
03047       }
03048       ast_stopstream(chan);
03049       *menu_mode = MENU_DISABLED;
03050       break;
03051 
03052    case '3': /* *83 (Admin) mute/unmute all non-admins */
03053       if(conf->gmuted) {
03054          conf->gmuted = 0;
03055          ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, &conf);
03056          if (!ast_streamfile(chan, "conf-now-unmuted", ast_channel_language(chan))) {
03057             ast_waitstream(chan, "");
03058          }
03059       } else {
03060          conf->gmuted = 1;
03061          ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_muted_cb, &conf);
03062          if (!ast_streamfile(chan, "conf-now-muted", ast_channel_language(chan))) {
03063             ast_waitstream(chan, "");
03064          }
03065       }
03066       ast_stopstream(chan);
03067       *menu_mode = MENU_DISABLED;
03068       break;
03069 
03070    case '4': /* *84 Record conference */
03071       if (conf->recording != MEETME_RECORD_ACTIVE) {
03072          ast_set_flag64(confflags, CONFFLAG_RECORDCONF);
03073          if (!conf->recordingfilename) {
03074             const char *var;
03075             ast_channel_lock(chan);
03076             if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
03077                conf->recordingfilename = ast_strdup(var);
03078             }
03079             if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
03080                conf->recordingformat = ast_strdup(var);
03081             }
03082             ast_channel_unlock(chan);
03083             if (!conf->recordingfilename) {
03084                snprintf(recordingtmp, recordingtmp_size, "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
03085                conf->recordingfilename = ast_strdup(recordingtmp);
03086             }
03087             if (!conf->recordingformat) {
03088                conf->recordingformat = ast_strdup("wav");
03089             }
03090             ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
03091             conf->confno, conf->recordingfilename, conf->recordingformat);
03092          }
03093 
03094          ast_mutex_lock(&conf->recordthreadlock);
03095          if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", cap_slin, NULL, chan, "pseudo", NULL)))) {
03096             struct dahdi_confinfo dahdic;
03097 
03098             ast_set_read_format(conf->lchan, ast_format_slin);
03099             ast_set_write_format(conf->lchan, ast_format_slin);
03100             dahdic.chan = 0;
03101             dahdic.confno = conf->dahdiconf;
03102             dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
03103             if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) {
03104                ast_log(LOG_WARNING, "Error starting listen channel\n");
03105                ast_hangup(conf->lchan);
03106                conf->lchan = NULL;
03107             } else {
03108                ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
03109             }
03110          }
03111          ast_mutex_unlock(&conf->recordthreadlock);
03112          if (!ast_streamfile(chan, "conf-now-recording", ast_channel_language(chan))) {
03113             ast_waitstream(chan, "");
03114          }
03115       }
03116 
03117       ast_stopstream(chan);
03118       *menu_mode = MENU_DISABLED;
03119       break;
03120 
03121    case '8': /* *88 Exit the menu and return to the conference... without an error message */
03122       ast_stopstream(chan);
03123       *menu_mode = MENU_DISABLED;
03124       break;
03125 
03126    default:
03127       if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
03128          ast_waitstream(chan, "");
03129       }
03130       ast_stopstream(chan);
03131       *menu_mode = MENU_DISABLED;
03132       break;
03133    }
03134 }
03135 
03136 /*! \internal
03137  * \brief Processes menu options for the various menu types (accessible through the 's' option for app_meetme)
03138  *
03139  * \param menu_mode a pointer to the currently active menu_mode.
03140  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
03141  * \param conf the active conference for which the user has called the menu from.
03142  * \param confflags flags used by conf for various options
03143  * \param chan ast_channel belonging to the user who called the menu
03144  * \param user which meetme conference user invoked the menu
03145  * \param recordingtmp character buffer which may hold the name of the conference recording file
03146  */
03147 static void meetme_menu(enum menu_modes *menu_mode, int *dtmf,
03148    struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan,
03149    struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size,
03150    struct ast_format_cap *cap_slin)
03151 {
03152    switch (*menu_mode) {
03153    case MENU_DISABLED:
03154       break;
03155    case MENU_NORMAL:
03156       meetme_menu_normal(menu_mode, dtmf, conf, confflags, chan, user);
03157       break;
03158    case MENU_ADMIN:
03159       meetme_menu_admin(menu_mode, dtmf, conf, confflags, chan, user);
03160       /* Admin Menu is capable of branching into another menu, in which case it will reset dtmf and change the menu mode. */
03161       if (*menu_mode != MENU_ADMIN_EXTENDED || (*dtmf <= 0)) {
03162          break;
03163       }
03164    case MENU_ADMIN_EXTENDED:
03165       meetme_menu_admin_extended(menu_mode, dtmf, conf, confflags, chan, user,
03166          recordingtmp, recordingtmp_size, cap_slin);
03167       break;
03168    }
03169 }
03170 
03171 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
03172 {
03173    struct ast_conf_user *user = NULL;
03174    int fd;
03175    struct dahdi_confinfo dahdic, dahdic_empty;
03176    struct ast_frame *f;
03177    struct ast_channel *c;
03178    struct ast_frame fr;
03179    int outfd;
03180    int ms;
03181    int nfds;
03182    int res;
03183    int retrydahdi;
03184    int origfd;
03185    int musiconhold = 0, mohtempstopped = 0;
03186    int firstpass = 0;
03187    int lastmarked = 0;
03188    int currentmarked = 0;
03189    int ret = -1;
03190    int x;
03191    enum menu_modes menu_mode = MENU_DISABLED;
03192    int talkreq_manager = 0;
03193    int using_pseudo = 0;
03194    int duration = 20;
03195    int sent_event = 0;
03196    int checked = 0;
03197    int announcement_played = 0;
03198    struct timeval now;
03199    struct ast_dsp *dsp = NULL;
03200    struct ast_app *agi_app;
03201    char *agifile, *mod_speex;
03202    const char *agifiledefault = "conf-background.agi", *tmpvar;
03203    char meetmesecs[30] = "";
03204    char exitcontext[AST_MAX_CONTEXT] = "";
03205    char recordingtmp[AST_MAX_EXTENSION] = "";
03206    char members[10] = "";
03207    int dtmf = 0, opt_waitmarked_timeout = 0;
03208    time_t timeout = 0;
03209    struct dahdi_bufferinfo bi;
03210    char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
03211    char *buf = __buf + AST_FRIENDLY_OFFSET;
03212    char *exitkeys = NULL;
03213    unsigned int calldurationlimit = 0;
03214    long timelimit = 0;
03215    long play_warning = 0;
03216    long warning_freq = 0;
03217    const char *warning_sound = NULL;
03218    const char *end_sound = NULL;
03219    char *parse;
03220    long time_left_ms = 0;
03221    struct timeval nexteventts = { 0, };
03222    int to;
03223    int setusercount = 0;
03224    int confsilence = 0, totalsilence = 0;
03225    char *mailbox, *context;
03226    struct ast_format_cap *cap_slin = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
03227 
03228    if (!cap_slin) {
03229       goto conf_run_cleanup;
03230    }
03231    ast_format_cap_append(cap_slin, ast_format_slin, 0);
03232 
03233    if (!(user = ao2_alloc(sizeof(*user), NULL))) {
03234       goto conf_run_cleanup;
03235    }
03236 
03237    /* Possible timeout waiting for marked user */
03238    if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
03239       !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
03240       (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
03241       (opt_waitmarked_timeout > 0)) {
03242       timeout = time(NULL) + opt_waitmarked_timeout;
03243    }
03244 
03245    if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
03246       calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
03247       ast_verb(3, "Setting call duration limit to %u seconds.\n", calldurationlimit);
03248    }
03249 
03250    if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
03251       char *limit_str, *warning_str, *warnfreq_str;
03252       const char *var;
03253 
03254       parse = optargs[OPT_ARG_DURATION_LIMIT];
03255       limit_str = strsep(&parse, ":");
03256       warning_str = strsep(&parse, ":");
03257       warnfreq_str = parse;
03258 
03259       timelimit = atol(limit_str);
03260       if (warning_str)
03261          play_warning = atol(warning_str);
03262       if (warnfreq_str)
03263          warning_freq = atol(warnfreq_str);
03264 
03265       if (!timelimit) {
03266          timelimit = play_warning = warning_freq = 0;
03267          warning_sound = NULL;
03268       } else if (play_warning > timelimit) {
03269          if (!warning_freq) {
03270             play_warning = 0;
03271          } else {
03272             while (play_warning > timelimit)
03273                play_warning -= warning_freq;
03274             if (play_warning < 1)
03275                play_warning = warning_freq = 0;
03276          }
03277       }
03278 
03279       ast_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
03280       if (play_warning) {
03281          ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
03282       }
03283       if (warning_freq) {
03284          ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
03285       }
03286 
03287       ast_channel_lock(chan);
03288       if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
03289          var = ast_strdupa(var);
03290       }
03291       ast_channel_unlock(chan);
03292 
03293       warning_sound = var ? var : "timeleft";
03294 
03295       ast_channel_lock(chan);
03296       if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
03297          var = ast_strdupa(var);
03298       }
03299       ast_channel_unlock(chan);
03300 
03301       end_sound = var ? var : NULL;
03302 
03303       /* undo effect of S(x) in case they are both used */
03304       calldurationlimit = 0;
03305       /* more efficient do it like S(x) does since no advanced opts */
03306       if (!play_warning && !end_sound && timelimit) {
03307          calldurationlimit = timelimit / 1000;
03308          timelimit = play_warning = warning_freq = 0;
03309       } else {
03310          ast_debug(2, "Limit Data for this call:\n");
03311          ast_debug(2, "- timelimit     = %ld\n", timelimit);
03312          ast_debug(2, "- play_warning  = %ld\n", play_warning);
03313          ast_debug(2, "- warning_freq  = %ld\n", warning_freq);
03314          ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
03315          ast_debug(2, "- end_sound     = %s\n", end_sound ? end_sound : "UNDEF");
03316       }
03317    }
03318 
03319    /* Get exit keys */
03320    if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
03321       if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
03322          exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
03323       else
03324          exitkeys = ast_strdupa("#"); /* Default */
03325    }
03326    
03327    if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
03328       if (!conf->recordingfilename) {
03329          const char *var;
03330          ast_channel_lock(chan);
03331          if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
03332             conf->recordingfilename = ast_strdup(var);
03333          }
03334          if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
03335             conf->recordingformat = ast_strdup(var);
03336          }
03337          ast_channel_unlock(chan);
03338          if (!conf->recordingfilename) {
03339             snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
03340             conf->recordingfilename = ast_strdup(recordingtmp);
03341          }
03342          if (!conf->recordingformat) {
03343             conf->recordingformat = ast_strdup("wav");
03344          }
03345          ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
03346                 conf->confno, conf->recordingfilename, conf->recordingformat);
03347       }
03348    }
03349 
03350    ast_mutex_lock(&conf->recordthreadlock);
03351    if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
03352       ((conf->lchan = ast_request("DAHDI", cap_slin, NULL, chan, "pseudo", NULL)))) {
03353       ast_set_read_format(conf->lchan, ast_format_slin);
03354       ast_set_write_format(conf->lchan, ast_format_slin);
03355       dahdic.chan = 0;
03356       dahdic.confno = conf->dahdiconf;
03357       dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
03358       if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) {
03359          ast_log(LOG_WARNING, "Error starting listen channel\n");
03360          ast_hangup(conf->lchan);
03361          conf->lchan = NULL;
03362       } else {
03363          ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
03364       }
03365    }
03366    ast_mutex_unlock(&conf->recordthreadlock);
03367 
03368    ast_mutex_lock(&conf->announcethreadlock);
03369    if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03370       ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) {
03371       ast_mutex_init(&conf->announcelistlock);
03372       AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
03373       ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
03374    }
03375    ast_mutex_unlock(&conf->announcethreadlock);
03376 
03377    time(&user->jointime);
03378    
03379    user->timelimit = timelimit;
03380    user->play_warning = play_warning;
03381    user->warning_freq = warning_freq;
03382    user->warning_sound = warning_sound;
03383    user->end_sound = end_sound;  
03384    
03385    if (calldurationlimit > 0) {
03386       time(&user->kicktime);
03387       user->kicktime = user->kicktime + calldurationlimit;
03388    }
03389    
03390    if (ast_tvzero(user->start_time))
03391       user->start_time = ast_tvnow();
03392    time_left_ms = user->timelimit;
03393    
03394    if (user->timelimit) {
03395       nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
03396       nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
03397    }
03398 
03399    if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
03400       /* Sorry, but this conference is locked! */  
03401       if (!ast_streamfile(chan, "conf-locked", ast_channel_language(chan)))
03402          ast_waitstream(chan, "");
03403       goto outrun;
03404    }
03405 
03406       ast_mutex_lock(&conf->playlock);
03407 
03408    if (rt_schedule && conf->maxusers) {
03409       if (conf->users >= conf->maxusers) {
03410          /* Sorry, but this confernce has reached the participant limit! */   
03411          ast_mutex_unlock(&conf->playlock);
03412          if (!ast_streamfile(chan, "conf-full", ast_channel_language(chan)))
03413             ast_waitstream(chan, "");
03414          goto outrun;
03415       }
03416    }
03417 
03418    ao2_lock(conf->usercontainer);
03419    ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
03420    user->user_no++;
03421    ao2_link(conf->usercontainer, user);
03422    ao2_unlock(conf->usercontainer);
03423 
03424    user->chan = chan;
03425    user->userflags = *confflags;
03426    user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
03427    user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
03428    user->talking = -1;
03429 
03430    ast_mutex_unlock(&conf->playlock);
03431 
03432    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC))) {
03433       char destdir[PATH_MAX];
03434 
03435       snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
03436 
03437       if (ast_mkdir(destdir, 0777) != 0) {
03438          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
03439          goto outrun;
03440       }
03441 
03442       if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
03443          context = ast_strdupa(optargs[OPT_ARG_INTROUSER_VMREC]);
03444          mailbox = strsep(&context, "@");
03445 
03446          if (ast_strlen_zero(mailbox)) {
03447             /* invalid input, clear the v flag*/
03448             ast_clear_flag64(confflags,CONFFLAG_INTROUSER_VMREC);
03449             ast_log(LOG_WARNING,"You must specify a mailbox in the v() option\n");
03450          } else {
03451             if (ast_strlen_zero(context)) {
03452                 context = "default";
03453             }
03454             /* if there is no mailbox we don't need to do this logic  */
03455             snprintf(user->namerecloc, sizeof(user->namerecloc),
03456                 "%s/voicemail/%s/%s/greet",ast_config_AST_SPOOL_DIR,context,mailbox);
03457 
03458             /* if the greeting doesn't exist then use the temp file method instead, clear flag v */
03459             if (!ast_fileexists(user->namerecloc, NULL, NULL)){
03460                snprintf(user->namerecloc, sizeof(user->namerecloc),
03461                    "%s/meetme-username-%s-%d", destdir,
03462                    conf->confno, user->user_no);
03463                ast_clear_flag64(confflags, CONFFLAG_INTROUSER_VMREC);
03464             }
03465          }
03466       } else {
03467          snprintf(user->namerecloc, sizeof(user->namerecloc),
03468              "%s/meetme-username-%s-%d", destdir,
03469              conf->confno, user->user_no);
03470       }
03471 
03472       res = 0;
03473       if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) && !ast_fileexists(user->namerecloc, NULL, NULL))
03474          res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
03475       else if (ast_test_flag64(confflags, CONFFLAG_INTROUSER) && !ast_fileexists(user->namerecloc, NULL, NULL))
03476          res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
03477       if (res == -1)
03478          goto outrun;
03479 
03480    }
03481 
03482    ast_mutex_lock(&conf->playlock);
03483 
03484    if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
03485       conf->markedusers++;
03486    conf->users++;
03487    if (rt_log_members) {
03488       /* Update table */
03489       snprintf(members, sizeof(members), "%d", conf->users);
03490       ast_realtime_require_field("meetme",
03491          "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
03492          "members", RQ_UINTEGER1, strlen(members),
03493          NULL);
03494       ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
03495    }
03496    setusercount = 1;
03497 
03498    /* This device changed state now - if this is the first user */
03499    if (conf->users == 1)
03500       ast_devstate_changed(AST_DEVICE_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
03501 
03502    ast_mutex_unlock(&conf->playlock);
03503 
03504    /* return the unique ID of the conference */
03505    pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
03506 
03507    if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
03508       ast_channel_lock(chan);
03509       if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
03510          ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
03511       } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))) {
03512          ast_copy_string(exitcontext, ast_channel_macrocontext(chan), sizeof(exitcontext));
03513       } else {
03514          ast_copy_string(exitcontext, ast_channel_context(chan), sizeof(exitcontext));
03515       }
03516       ast_channel_unlock(chan);
03517    }
03518 
03519    /* Play an arbitrary intro message */
03520    if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
03521          !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
03522       if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], ast_channel_language(chan))) {
03523          ast_waitstream(chan, "");
03524       }
03525    }
03526 
03527    if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
03528       if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
03529          if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan)))
03530             ast_waitstream(chan, "");
03531       if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
03532          if (!ast_streamfile(chan, "conf-waitforleader", ast_channel_language(chan)))
03533             ast_waitstream(chan, "");
03534    }
03535 
03536    if (ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
03537       int keepplaying = 1;
03538 
03539       if (conf->users == 2) { 
03540          if (!ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
03541             res = ast_waitstream(chan, AST_DIGIT_ANY);
03542             ast_stopstream(chan);
03543             if (res > 0)
03544                keepplaying = 0;
03545             else if (res == -1)
03546                goto outrun;
03547          }
03548       } else { 
03549          if (!ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
03550             res = ast_waitstream(chan, AST_DIGIT_ANY);
03551             ast_stopstream(chan);
03552             if (res > 0)
03553                keepplaying = 0;
03554             else if (res == -1)
03555                goto outrun;
03556          }
03557          if (keepplaying) {
03558             res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03559             if (res > 0)
03560                keepplaying = 0;
03561             else if (res == -1)
03562                goto outrun;
03563          }
03564          if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
03565             res = ast_waitstream(chan, AST_DIGIT_ANY);
03566             ast_stopstream(chan);
03567             if (res > 0)
03568                keepplaying = 0;
03569             else if (res == -1) 
03570                goto outrun;
03571          }
03572       }
03573    }
03574 
03575    if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
03576       /* We're leaving this alone until the state gets changed to up */
03577       ast_indicate(chan, -1);
03578    }
03579 
03580    if (ast_set_write_format(chan, ast_format_slin) < 0) {
03581       ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", ast_channel_name(chan));
03582       goto outrun;
03583    }
03584 
03585    if (ast_set_read_format(chan, ast_format_slin) < 0) {
03586       ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", ast_channel_name(chan));
03587       goto outrun;
03588    }
03589 
03590    /* Reduce background noise from each participant */
03591    if (!ast_test_flag64(confflags, CONFFLAG_DONT_DENOISE) &&
03592       (mod_speex = ast_module_helper("", "func_speex", 0, 0, 0, 0))) {
03593       ast_free(mod_speex);
03594       ast_func_write(chan, "DENOISE(rx)", "on");
03595    }
03596 
03597    retrydahdi = (strcasecmp(ast_channel_tech(chan)->type, "DAHDI") || (ast_channel_audiohooks(chan) || ast_channel_monitor(chan)) ? 1 : 0);
03598    user->dahdichannel = !retrydahdi;
03599 
03600  dahdiretry:
03601    origfd = ast_channel_fd(chan, 0);
03602    if (retrydahdi) {
03603       /* open pseudo in non-blocking mode */
03604       fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
03605       if (fd < 0) {
03606          ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
03607          goto outrun;
03608       }
03609       using_pseudo = 1;
03610       /* Setup buffering information */
03611       memset(&bi, 0, sizeof(bi));
03612       bi.bufsize = CONF_SIZE / 2;
03613       bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
03614       bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
03615       bi.numbufs = audio_buffers;
03616       if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
03617          ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
03618          close(fd);
03619          goto outrun;
03620       }
03621       x = 1;
03622       if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
03623          ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
03624          close(fd);
03625          goto outrun;
03626       }
03627       nfds = 1;
03628    } else {
03629       /* XXX Make sure we're not running on a pseudo channel XXX */
03630       fd = ast_channel_fd(chan, 0);
03631       nfds = 0;
03632    }
03633    memset(&dahdic, 0, sizeof(dahdic));
03634    memset(&dahdic_empty, 0, sizeof(dahdic_empty));
03635    /* Check to see if we're in a conference... */
03636    dahdic.chan = 0;
03637    if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
03638       ast_log(LOG_WARNING, "Error getting conference\n");
03639       close(fd);
03640       goto outrun;
03641    }
03642    if (dahdic.confmode) {
03643       /* Whoa, already in a conference...  Retry... */
03644       if (!retrydahdi) {
03645          ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
03646          retrydahdi = 1;
03647          goto dahdiretry;
03648       }
03649    }
03650    memset(&dahdic, 0, sizeof(dahdic));
03651    /* Add us to the conference */
03652    dahdic.chan = 0;
03653    dahdic.confno = conf->dahdiconf;
03654 
03655    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
03656          ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) || ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)) && conf->users > 1) {
03657       struct announce_listitem *item;
03658       if (!(item = ao2_alloc(sizeof(*item), NULL)))
03659          goto outrun;
03660       ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
03661       ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
03662       item->confchan = conf->chan;
03663       item->confusers = conf->users;
03664       if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
03665          item->vmrec = 1;
03666       }
03667       item->announcetype = CONF_HASJOIN;
03668       ast_mutex_lock(&conf->announcelistlock);
03669       ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
03670       AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
03671       ast_cond_signal(&conf->announcelist_addition);
03672       ast_mutex_unlock(&conf->announcelistlock);
03673 
03674       while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
03675          ;
03676       }
03677       ao2_ref(item, -1);
03678    }
03679 
03680    if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && !conf->markedusers)
03681       dahdic.confmode = DAHDI_CONF_CONF;
03682    else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
03683       dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
03684    else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
03685       dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
03686    else
03687       dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
03688 
03689    if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03690       ast_log(LOG_WARNING, "Error setting conference\n");
03691       close(fd);
03692       goto outrun;
03693    }
03694    ast_debug(1, "Placed channel %s in DAHDI conf %d\n", ast_channel_name(chan), conf->dahdiconf);
03695 
03696    if (!sent_event) {
03697       meetme_stasis_generate_msg(conf, chan, user, meetme_join_type(), NULL);
03698       sent_event = 1;
03699    }
03700 
03701    if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
03702       !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03703       firstpass = 1;
03704       if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
03705          if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
03706             (conf->markedusers >= 1))) {
03707             conf_play(chan, conf, ENTER);
03708          }
03709    }
03710 
03711    conf_flush(fd, chan);
03712 
03713    if (dsp)
03714       ast_dsp_free(dsp);
03715 
03716    if (!(dsp = ast_dsp_new())) {
03717       ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
03718       res = -1;
03719    }
03720 
03721    if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
03722       /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
03723          or use default filename of conf-background.agi */
03724 
03725       ast_channel_lock(chan);
03726       if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
03727          agifile = ast_strdupa(tmpvar);
03728       } else {
03729          agifile = ast_strdupa(agifiledefault);
03730       }
03731       ast_channel_unlock(chan);
03732       
03733       if (user->dahdichannel) {
03734          /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
03735          x = 1;
03736          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03737       }
03738       /* Find a pointer to the agi app and execute the script */
03739       agi_app = pbx_findapp("agi");
03740       if (agi_app) {
03741          ret = pbx_exec(chan, agi_app, agifile);
03742       } else {
03743          ast_log(LOG_WARNING, "Could not find application (agi)\n");
03744          ret = -2;
03745       }
03746       if (user->dahdichannel) {
03747          /*  Remove CONFMUTE mode on DAHDI channel */
03748          x = 0;
03749          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03750       }
03751    } else {
03752       int lastusers = conf->users;
03753       if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
03754          /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
03755          x = 1;
03756          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03757       }
03758 
03759       for (;;) {
03760          int menu_was_active = 0;
03761 
03762          outfd = -1;
03763          ms = -1;
03764          now = ast_tvnow();
03765 
03766          if (rt_schedule && conf->endtime) {
03767             char currenttime[32];
03768             long localendtime = 0;
03769             int extended = 0;
03770             struct ast_tm tm;
03771             struct ast_variable *var, *origvar;
03772             struct timeval tmp;
03773 
03774             if (now.tv_sec % 60 == 0) {
03775                if (!checked) {
03776                   ast_localtime(&now, &tm, NULL);
03777                   ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
03778                   var = origvar = ast_load_realtime("meetme", "confno",
03779                      conf->confno, "starttime <=", currenttime,
03780                       "endtime >=", currenttime, NULL);
03781 
03782                   for ( ; var; var = var->next) {
03783                      if (!strcasecmp(var->name, "endtime")) {
03784                         struct ast_tm endtime_tm;
03785                         ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
03786                         tmp = ast_mktime(&endtime_tm, NULL);
03787                         localendtime = tmp.tv_sec;
03788                      }
03789                   }
03790                   ast_variables_destroy(origvar);
03791 
03792                   /* A conference can be extended from the
03793                      Admin/User menu or by an external source */
03794                   if (localendtime > conf->endtime){
03795                      conf->endtime = localendtime;
03796                      extended = 1;
03797                   }
03798 
03799                   if (conf->endtime && (now.tv_sec >= conf->endtime)) {
03800                      ast_verbose("Quitting time...\n");
03801                      goto outrun;
03802                   }
03803 
03804                   if (!announcement_played && conf->endalert) {
03805                      if (now.tv_sec + conf->endalert >= conf->endtime) {
03806                         if (!ast_streamfile(chan, "conf-will-end-in", ast_channel_language(chan)))
03807                            ast_waitstream(chan, "");
03808                         ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", ast_channel_language(chan));
03809                         if (!ast_streamfile(chan, "minutes", ast_channel_language(chan)))
03810                            ast_waitstream(chan, "");
03811                         if (musiconhold) {
03812                            conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03813                         }
03814                         announcement_played = 1;
03815                      }
03816                   }
03817 
03818                   if (extended) {
03819                      announcement_played = 0;
03820                   }
03821 
03822                   checked = 1;
03823                }
03824             } else {
03825                checked = 0;
03826             }
03827          }
03828 
03829          if (user->kicktime && (user->kicktime <= now.tv_sec)) {
03830             if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03831                ret = 0;
03832             } else {
03833                ret = -1;
03834             }
03835             break;
03836          }
03837   
03838          to = -1;
03839          if (user->timelimit) {
03840             int minutes = 0, seconds = 0, remain = 0;
03841  
03842             to = ast_tvdiff_ms(nexteventts, now);
03843             if (to < 0) {
03844                to = 0;
03845             }
03846             time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
03847             if (time_left_ms < to) {
03848                to = time_left_ms;
03849             }
03850    
03851             if (time_left_ms <= 0) {
03852                if (user->end_sound) {                 
03853                   res = ast_streamfile(chan, user->end_sound, ast_channel_language(chan));
03854                   res = ast_waitstream(chan, "");
03855                }
03856                if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03857                   ret = 0;
03858                } else {
03859                   ret = -1;
03860                }
03861                break;
03862             }
03863             
03864             if (!to) {
03865                if (time_left_ms >= 5000) {                  
03866                   
03867                   remain = (time_left_ms + 500) / 1000;
03868                   if (remain / 60 >= 1) {
03869                      minutes = remain / 60;
03870                      seconds = remain % 60;
03871                   } else {
03872                      seconds = remain;
03873                   }
03874                   
03875                   /* force the time left to round up if appropriate */
03876                   if (user->warning_sound && user->play_warning) {
03877                      if (!strcmp(user->warning_sound, "timeleft")) {
03878                         
03879                         res = ast_streamfile(chan, "vm-youhave", ast_channel_language(chan));
03880                         res = ast_waitstream(chan, "");
03881                         if (minutes) {
03882                            res = ast_say_number(chan, minutes, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03883                            res = ast_streamfile(chan, "queue-minutes", ast_channel_language(chan));
03884                            res = ast_waitstream(chan, "");
03885                         }
03886                         if (seconds) {
03887                            res = ast_say_number(chan, seconds, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03888                            res = ast_streamfile(chan, "queue-seconds", ast_channel_language(chan));
03889                            res = ast_waitstream(chan, "");
03890                         }
03891                      } else {
03892                         res = ast_streamfile(chan, user->warning_sound, ast_channel_language(chan));
03893                         res = ast_waitstream(chan, "");
03894                      }
03895                      if (musiconhold) {
03896                         conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03897                      }
03898                   }
03899                }
03900                if (user->warning_freq) {
03901                   nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
03902                } else {
03903                   nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
03904                }
03905             }
03906          }
03907 
03908          now = ast_tvnow();
03909          if (timeout && now.tv_sec >= timeout) {
03910             if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03911                ret = 0;
03912             } else {
03913                ret = -1;
03914             }
03915             break;
03916          }
03917 
03918          /* if we have just exited from the menu, and the user had a channel-driver
03919             volume adjustment, restore it
03920          */
03921          if (!menu_mode && menu_was_active && user->listen.desired && !user->listen.actual) {
03922             set_talk_volume(user, user->listen.desired);
03923          }
03924 
03925          menu_was_active = menu_mode;
03926 
03927          currentmarked = conf->markedusers;
03928          if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03929              ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
03930              ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
03931              lastmarked == 0) {
03932             if (currentmarked == 1 && conf->users > 1) {
03933                ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03934                if (conf->users - 1 == 1) {
03935                   if (!ast_streamfile(chan, "conf-userwilljoin", ast_channel_language(chan))) {
03936                      ast_waitstream(chan, "");
03937                   }
03938                } else {
03939                   if (!ast_streamfile(chan, "conf-userswilljoin", ast_channel_language(chan))) {
03940                      ast_waitstream(chan, "");
03941                   }
03942                }
03943             }
03944             if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
03945                if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
03946                   ast_waitstream(chan, "");
03947                }
03948             }
03949          }
03950 
03951          /* Update the struct with the actual confflags */
03952          user->userflags = *confflags;
03953 
03954          if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
03955             if (currentmarked == 0) {
03956                if (lastmarked != 0) {
03957                   if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
03958                      if (!ast_streamfile(chan, "conf-leaderhasleft", ast_channel_language(chan))) {
03959                         ast_waitstream(chan, "");
03960                      }
03961                   }
03962                   if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
03963                      if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03964                         ret = 0;
03965                      }
03966                      break;
03967                   } else {
03968                      dahdic.confmode = DAHDI_CONF_CONF;
03969                      if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03970                         ast_log(LOG_WARNING, "Error setting conference\n");
03971                         close(fd);
03972                         goto outrun;
03973                      }
03974                   }
03975                }
03976                if (!musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
03977                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03978                   musiconhold = 1;
03979                }
03980             } else if (currentmarked >= 1 && lastmarked == 0) {
03981                /* Marked user entered, so cancel timeout */
03982                timeout = 0;
03983                if (ast_test_flag64(confflags, CONFFLAG_MONITOR)) {
03984                   dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
03985                } else if (ast_test_flag64(confflags, CONFFLAG_TALKER)) {
03986                   dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
03987                } else {
03988                   dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
03989                }
03990                if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03991                   ast_log(LOG_WARNING, "Error setting conference\n");
03992                   close(fd);
03993                   goto outrun;
03994                }
03995                if (musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
03996                   ast_moh_stop(chan);
03997                   musiconhold = 0;
03998                }
03999                if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && 
04000                   !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
04001                   if (!ast_streamfile(chan, "conf-placeintoconf", ast_channel_language(chan))) {
04002                      ast_waitstream(chan, "");
04003                   }
04004                   conf_play(chan, conf, ENTER);
04005                }
04006             }
04007          }
04008 
04009          /* trying to add moh for single person conf */
04010          if (ast_test_flag64(confflags, CONFFLAG_MOH) && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
04011             if (conf->users == 1) {
04012                if (!musiconhold) {
04013                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
04014                   musiconhold = 1;
04015                } 
04016             } else {
04017                if (musiconhold) {
04018                   ast_moh_stop(chan);
04019                   musiconhold = 0;
04020                }
04021             }
04022          }
04023          
04024          /* Leave if the last marked user left */
04025          if (currentmarked == 0 && lastmarked != 0 && ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
04026             if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
04027                ret = 0;
04028             } else {
04029                ret = -1;
04030             }
04031             break;
04032          }
04033 
04034          /* Throw a TestEvent if a user exit did not cause this user to leave the conference */
04035          if (conf->users != lastusers) {
04036             if (conf->users < lastusers) {
04037                ast_test_suite_event_notify("NOEXIT", "Message: CONFFLAG_MARKEDEXIT\r\nLastUsers: %d\r\nUsers: %d", lastusers, conf->users);
04038             }
04039             lastusers = conf->users;
04040          }
04041 
04042          /* Check if my modes have changed */
04043 
04044          /* If I should be muted but am still talker, mute me */
04045          if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
04046             RAII_VAR(struct ast_json *, status_blob, status_to_json(1), ast_json_unref);
04047             dahdic.confmode ^= DAHDI_CONF_TALKER;
04048             if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
04049                ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
04050                ret = -1;
04051                break;
04052             }
04053 
04054             /* Indicate user is not talking anymore - change him to unmonitored state */
04055             if (ast_test_flag64(confflags,  (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER))) {
04056                set_user_talking(chan, conf, user, -1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
04057             }
04058             meetme_stasis_generate_msg(conf, chan, user, meetme_mute_type(), status_blob);
04059          }
04060 
04061          /* If I should be un-muted but am not talker, un-mute me */
04062          if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
04063             RAII_VAR(struct ast_json *, status_blob, status_to_json(0), ast_json_unref);
04064             dahdic.confmode |= DAHDI_CONF_TALKER;
04065             if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
04066                ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
04067                ret = -1;
04068                break;
04069             }
04070             meetme_stasis_generate_msg(conf, chan, user, meetme_mute_type(), status_blob);
04071          }
04072 
04073          if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
04074             (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
04075 
04076             RAII_VAR(struct ast_json *, status_blob, status_to_json(1), ast_json_unref);
04077             talkreq_manager = 1;
04078             meetme_stasis_generate_msg(conf, chan, user, meetme_talk_request_type(), status_blob);
04079          }
04080 
04081          if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
04082             !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
04083             RAII_VAR(struct ast_json *, status_blob, status_to_json(0), ast_json_unref);
04084             talkreq_manager = 0;
04085             meetme_stasis_generate_msg(conf, chan, user, meetme_talk_request_type(), status_blob);
04086          }
04087 
04088          /* If user have been hung up, exit the conference */
04089          if (user->adminflags & ADMINFLAG_HANGUP) {
04090             ret = 0;
04091             break;
04092          }
04093 
04094          /* If I have been kicked, exit the conference */
04095          if (user->adminflags & ADMINFLAG_KICKME) {
04096             /* You have been kicked. */
04097             if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && 
04098                !ast_streamfile(chan, "conf-kicked", ast_channel_language(chan))) {
04099                ast_waitstream(chan, "");
04100             }
04101             ret = 0;
04102             break;
04103          }
04104 
04105          /* Perform a hangup check here since ast_waitfor_nandfds will not always be able to get a channel after a hangup has occurred */
04106          if (ast_check_hangup(chan)) {
04107             break;
04108          }
04109 
04110          c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
04111 
04112          if (c) {
04113             char dtmfstr[2] = "";
04114 
04115             if (ast_channel_fd(c, 0) != origfd || (user->dahdichannel && (ast_channel_audiohooks(c) || ast_channel_monitor(c)))) {
04116                if (using_pseudo) {
04117                   /* Kill old pseudo */
04118                   close(fd);
04119                   using_pseudo = 0;
04120                }
04121                ast_debug(1, "Ooh, something swapped out under us, starting over\n");
04122                retrydahdi = (strcasecmp(ast_channel_tech(c)->type, "DAHDI") || (ast_channel_audiohooks(c) || ast_channel_monitor(c)) ? 1 : 0);
04123                user->dahdichannel = !retrydahdi;
04124                goto dahdiretry;
04125             }
04126             if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
04127                f = ast_read_noaudio(c);
04128             } else {
04129                f = ast_read(c);
04130             }
04131             if (!f) {
04132                break;
04133             }
04134             if (f->frametype == AST_FRAME_DTMF) {
04135                dtmfstr[0] = f->subclass.integer;
04136                dtmfstr[1] = '\0';
04137             }
04138 
04139             if ((f->frametype == AST_FRAME_VOICE) && (ast_format_cmp(f->subclass.format, ast_format_slin) == AST_FORMAT_CMP_EQUAL)) {
04140                if (user->talk.actual) {
04141                   ast_frame_adjust_volume(f, user->talk.actual);
04142                }
04143 
04144                if (ast_test_flag64(confflags, (CONFFLAG_OPTIMIZETALKER | CONFFLAG_MONITORTALKER))) {
04145                   if (user->talking == -1) {
04146                      user->talking = 0;
04147                   }
04148 
04149                   res = ast_dsp_silence(dsp, f, &totalsilence);
04150                   if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
04151                      set_user_talking(chan, conf, user, 1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
04152                   }
04153 
04154                   if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
04155                      set_user_talking(chan, conf, user, 0, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
04156                   }
04157                }
04158                if (using_pseudo) {
04159                   /* Absolutely do _not_ use careful_write here...
04160                      it is important that we read data from the channel
04161                      as fast as it arrives, and feed it into the conference.
04162                      The buffering in the pseudo channel will take care of any
04163                      timing differences, unless they are so drastic as to lose
04164                      audio frames (in which case carefully writing would only
04165                      have delayed the audio even further).
04166                   */
04167                   /* As it turns out, we do want to use careful write.  We just
04168                      don't want to block, but we do want to at least *try*
04169                      to write out all the samples.
04170                    */
04171                   if (user->talking || !ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER)) {
04172                      careful_write(fd, f->data.ptr, f->datalen, 0);
04173                   }
04174                }
04175             } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '*') && ast_test_flag64(confflags, CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_mode)) {
04176                if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
04177                   conf_queue_dtmf(conf, user, f);
04178                }
04179                /* Take out of conference */
04180                if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
04181                   ast_log(LOG_WARNING, "Error setting conference\n");
04182                   close(fd);
04183                   ast_frfree(f);
04184                   goto outrun;
04185                }
04186 
04187                /* if we are entering the menu, and the user has a channel-driver
04188                   volume adjustment, clear it
04189                */
04190                if (!menu_mode && user->talk.desired && !user->talk.actual) {
04191                   set_talk_volume(user, 0);
04192                }
04193 
04194                if (musiconhold) {
04195                   ast_moh_stop(chan);
04196                } else if (!menu_mode) {
04197                   char *menu_to_play;
04198                   if (ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
04199                      menu_mode = MENU_ADMIN;
04200                      menu_to_play = "conf-adminmenu-18";
04201                   } else {
04202                      menu_mode = MENU_NORMAL;
04203                      menu_to_play = "conf-usermenu-162";
04204                   }
04205 
04206                   if (!ast_streamfile(chan, menu_to_play, ast_channel_language(chan))) {
04207                      dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
04208                      ast_stopstream(chan);
04209                   } else {
04210                      dtmf = 0;
04211                   }
04212                } else {
04213                   dtmf = f->subclass.integer;
04214                }
04215 
04216                if (dtmf > 0) {
04217                   meetme_menu(&menu_mode, &dtmf, conf, confflags,
04218                      chan, user, recordingtmp, sizeof(recordingtmp), cap_slin);
04219                }
04220 
04221                if (musiconhold && !menu_mode) {
04222                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
04223                }
04224 
04225                /* Put back into conference */
04226                if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
04227                   ast_log(LOG_WARNING, "Error setting conference\n");
04228                   close(fd);
04229                   ast_frfree(f);
04230                   goto outrun;
04231                }
04232 
04233                conf_flush(fd, chan);
04234             /*
04235              * Since options using DTMF could absorb DTMF meant for the
04236              * conference menu, we have to check them after the menu.
04237              */
04238             } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
04239                if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
04240                   conf_queue_dtmf(conf, user, f);
04241                }
04242 
04243                if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
04244                   ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
04245                   ret = 0;
04246                   ast_frfree(f);
04247                   break;
04248                } else {
04249                   ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
04250                }
04251             } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_KEYEXIT) &&
04252                (strchr(exitkeys, f->subclass.integer))) {
04253                pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
04254 
04255                if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
04256                   conf_queue_dtmf(conf, user, f);
04257                }
04258                ret = 0;
04259                ast_frfree(f);
04260                break;
04261             } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
04262                && ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
04263                conf_queue_dtmf(conf, user, f);
04264             } else if (ast_test_flag64(confflags, CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
04265                switch (f->subclass.integer) {
04266                case AST_CONTROL_HOLD:
04267                   sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
04268                   break;
04269                default:
04270                   break;
04271                }
04272             } else if (f->frametype == AST_FRAME_NULL) {
04273                /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
04274             } else if (f->frametype == AST_FRAME_CONTROL) {
04275                switch (f->subclass.integer) {
04276                case AST_CONTROL_BUSY:
04277                case AST_CONTROL_CONGESTION:
04278                   ast_frfree(f);
04279                   goto outrun;
04280                   break;
04281                default:
04282                   ast_debug(1,
04283                      "Got ignored control frame on channel %s, f->frametype=%u,f->subclass=%d\n",
04284                      ast_channel_name(chan), f->frametype, f->subclass.integer);
04285                }
04286             } else {
04287                ast_debug(1,
04288                   "Got unrecognized frame on channel %s, f->frametype=%u,f->subclass=%d\n",
04289                   ast_channel_name(chan), f->frametype, f->subclass.integer);
04290             }
04291             ast_frfree(f);
04292          } else if (outfd > -1) {
04293             res = read(outfd, buf, CONF_SIZE);
04294             if (res > 0) {
04295                memset(&fr, 0, sizeof(fr));
04296                fr.frametype = AST_FRAME_VOICE;
04297                fr.subclass.format = ast_format_slin;
04298                fr.datalen = res;
04299                fr.samples = res / 2;
04300                fr.data.ptr = buf;
04301                fr.offset = AST_FRIENDLY_OFFSET;
04302                if (!user->listen.actual &&
04303                   (ast_test_flag64(confflags, CONFFLAG_MONITOR) ||
04304                    (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
04305                    (!user->talking && ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER))
04306                    )) {
04307                   int idx;
04308                   for (idx = 0; idx < AST_FRAME_BITS; idx++) {
04309                      if (ast_format_compatibility_format2bitfield(ast_channel_rawwriteformat(chan)) & (1 << idx)) {
04310                         break;
04311                      }
04312                   }
04313                   if (idx >= AST_FRAME_BITS) {
04314                      goto bailoutandtrynormal;
04315                   }
04316                   ast_mutex_lock(&conf->listenlock);
04317                   if (!conf->transframe[idx]) {
04318                      if (conf->origframe) {
04319                         if (musiconhold
04320                            && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)
04321                            && !ast_dsp_silence(dsp, conf->origframe, &confsilence)
04322                            && confsilence < MEETME_DELAYDETECTTALK) {
04323                            ast_moh_stop(chan);
04324                            mohtempstopped = 1;
04325                         }
04326                         if (!conf->transpath[idx]) {
04327                            conf->transpath[idx] = ast_translator_build_path(ast_channel_rawwriteformat(chan), ast_format_slin);
04328                         }
04329                         if (conf->transpath[idx]) {
04330                            conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
04331                            if (!conf->transframe[idx]) {
04332                               conf->transframe[idx] = &ast_null_frame;
04333                            }
04334                         }
04335                      }
04336                   }
04337                   if (conf->transframe[idx]) {
04338                      if ((conf->transframe[idx]->frametype != AST_FRAME_NULL) &&
04339                          can_write(chan, confflags)) {
04340                         struct ast_frame *cur;
04341                         /* the translator may have returned a list of frames, so
04342                            write each one onto the channel
04343                         */
04344                         for (cur = conf->transframe[idx]; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
04345                            if (ast_write(chan, cur)) {
04346                               ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", ast_channel_name(chan));
04347                               break;
04348                            }
04349                         }
04350                         if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
04351                            mohtempstopped = 0;
04352                            conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
04353                         }
04354                      }
04355                   } else {
04356                      ast_mutex_unlock(&conf->listenlock);
04357                      goto bailoutandtrynormal;
04358                   }
04359                   ast_mutex_unlock(&conf->listenlock);
04360                } else {
04361 bailoutandtrynormal:
04362                   if (musiconhold
04363                      && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)
04364                      && !ast_dsp_silence(dsp, &fr, &confsilence)
04365                      && confsilence < MEETME_DELAYDETECTTALK) {
04366                      ast_moh_stop(chan);
04367                      mohtempstopped = 1;
04368                   }
04369                   if (user->listen.actual) {
04370                      ast_frame_adjust_volume(&fr, user->listen.actual);
04371                   }
04372                   if (can_write(chan, confflags) && ast_write(chan, &fr) < 0) {
04373                      ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", ast_channel_name(chan));
04374                   }
04375                   if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
04376                      mohtempstopped = 0;
04377                      conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
04378                   }
04379                }
04380             } else {
04381                ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
04382             }
04383          }
04384          lastmarked = currentmarked;
04385       }
04386    }
04387 
04388    if (musiconhold) {
04389       ast_moh_stop(chan);
04390    }
04391    
04392    if (using_pseudo) {
04393       close(fd);
04394    } else {
04395       /* Take out of conference */
04396       dahdic.chan = 0;  
04397       dahdic.confno = 0;
04398       dahdic.confmode = 0;
04399       if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
04400          ast_log(LOG_WARNING, "Error setting conference\n");
04401       }
04402    }
04403 
04404    reset_volumes(user);
04405 
04406    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
04407       !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
04408       conf_play(chan, conf, LEAVE);
04409    }
04410 
04411    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_INTROUSER |CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC) && conf->users > 1) {
04412       struct announce_listitem *item;
04413       if (!(item = ao2_alloc(sizeof(*item), NULL)))
04414          goto outrun;
04415       ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
04416       ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
04417       item->confchan = conf->chan;
04418       item->confusers = conf->users;
04419       item->announcetype = CONF_HASLEFT;
04420       if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
04421          item->vmrec = 1;
04422       }
04423       ast_mutex_lock(&conf->announcelistlock);
04424       AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
04425       ast_cond_signal(&conf->announcelist_addition);
04426       ast_mutex_unlock(&conf->announcelistlock);
04427    } else if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW) && !ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC) && conf->users == 1) {
04428       /* Last person is leaving, so no reason to try and announce, but should delete the name recording */
04429       ast_filedelete(user->namerecloc, NULL);
04430    }
04431 
04432  outrun:
04433    AST_LIST_LOCK(&confs);
04434 
04435    if (dsp) {
04436       ast_dsp_free(dsp);
04437    }
04438    
04439    if (user->user_no) {
04440       /* Only cleanup users who really joined! */
04441       now = ast_tvnow();
04442 
04443       if (sent_event) {
04444          meetme_stasis_generate_msg(conf, chan, user, meetme_leave_type(), NULL);
04445       }
04446 
04447       if (setusercount) {
04448          conf->users--;
04449          if (rt_log_members) {
04450             /* Update table */
04451             snprintf(members, sizeof(members), "%d", conf->users);
04452             ast_realtime_require_field("meetme",
04453                "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
04454                "members", RQ_UINTEGER1, strlen(members),
04455                NULL);
04456             ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
04457          }
04458          if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
04459             conf->markedusers--;
04460          }
04461       }
04462       /* Remove ourselves from the container */
04463       ao2_unlink(conf->usercontainer, user); 
04464 
04465       /* Change any states */
04466       if (!conf->users) {
04467          ast_devstate_changed(AST_DEVICE_NOT_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
04468       }
04469 
04470       /* This flag is meant to kill a conference with only one participant remaining.  */
04471       if (conf->users == 1 && ast_test_flag64(confflags, CONFFLAG_KILL_LAST_MAN_STANDING)) {
04472          ao2_callback(conf->usercontainer, 0, user_set_hangup_cb, NULL);
04473       }
04474 
04475       /* Return the number of seconds the user was in the conf */
04476       snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
04477       pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
04478 
04479       /* Return the RealTime bookid for CDR linking */
04480       if (rt_schedule) {
04481          pbx_builtin_setvar_helper(chan, "MEETMEBOOKID", conf->bookid);
04482       }
04483    }
04484    ao2_ref(user, -1);
04485    AST_LIST_UNLOCK(&confs);
04486 
04487 
04488 conf_run_cleanup:
04489    ao2_cleanup(cap_slin);
04490 
04491    return ret;
04492 }
04493 
04494 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
04495             char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags, int *too_early, char **optargs)
04496 {
04497    struct ast_variable *var, *origvar;
04498    struct ast_conference *cnf;
04499 
04500    *too_early = 0;
04501 
04502    /* Check first in the conference list */
04503    AST_LIST_LOCK(&confs);
04504    AST_LIST_TRAVERSE(&confs, cnf, list) {
04505       if (!strcmp(confno, cnf->confno)) {
04506          break;
04507       }
04508    }
04509    if (cnf) {
04510       cnf->refcount += refcount;
04511    }
04512    AST_LIST_UNLOCK(&confs);
04513 
04514    if (!cnf) {
04515       char *pin = NULL, *pinadmin = NULL; /* For temp use */
04516       int maxusers = 0;
04517       struct timeval now;
04518       char recordingfilename[256] = "";
04519       char recordingformat[11] = "";
04520       char currenttime[32] = "";
04521       char eatime[32] = "";
04522       char bookid[51] = "";
04523       char recordingtmp[AST_MAX_EXTENSION] = "";
04524       char useropts[OPTIONS_LEN + 1] = ""; /* Used for RealTime conferences */
04525       char adminopts[OPTIONS_LEN + 1] = "";
04526       struct ast_tm tm, etm;
04527       struct timeval endtime = { .tv_sec = 0 };
04528       const char *var2;
04529 
04530       if (rt_schedule) {
04531          now = ast_tvnow();
04532 
04533          ast_localtime(&now, &tm, NULL);
04534          ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
04535 
04536          ast_debug(1, "Looking for conference %s that starts after %s\n", confno, currenttime);
04537 
04538          var = ast_load_realtime("meetme", "confno",
04539             confno, "starttime <= ", currenttime, "endtime >= ",
04540             currenttime, NULL);
04541 
04542          if (!var && fuzzystart) {
04543             now = ast_tvnow();
04544             now.tv_sec += fuzzystart;
04545 
04546             ast_localtime(&now, &tm, NULL);
04547             ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
04548             var = ast_load_realtime("meetme", "confno",
04549                confno, "starttime <= ", currenttime, "endtime >= ",
04550                currenttime, NULL);
04551          }
04552 
04553          if (!var && earlyalert) {
04554             now = ast_tvnow();
04555             now.tv_sec += earlyalert;
04556             ast_localtime(&now, &etm, NULL);
04557             ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
04558             var = ast_load_realtime("meetme", "confno",
04559                confno, "starttime <= ", eatime, "endtime >= ",
04560                currenttime, NULL);
04561             if (var) {
04562                *too_early = 1;
04563             }
04564          }
04565 
04566       } else {
04567           var = ast_load_realtime("meetme", "confno", confno, NULL);
04568       }
04569 
04570       if (!var) {
04571          return NULL;
04572       }
04573 
04574       if (rt_schedule && *too_early) {
04575          /* Announce that the caller is early and exit */
04576          if (!ast_streamfile(chan, "conf-has-not-started", ast_channel_language(chan))) {
04577             ast_waitstream(chan, "");
04578          }
04579          ast_variables_destroy(var);
04580          return NULL;
04581       }
04582 
04583       for (origvar = var; var; var = var->next) {
04584          if (!strcasecmp(var->name, "pin")) {
04585             pin = ast_strdupa(var->value);
04586          } else if (!strcasecmp(var->name, "adminpin")) {
04587             pinadmin = ast_strdupa(var->value);
04588          } else if (!strcasecmp(var->name, "bookId")) {
04589             ast_copy_string(bookid, var->value, sizeof(bookid));
04590          } else if (!strcasecmp(var->name, "opts")) {
04591             ast_copy_string(useropts, var->value, sizeof(char[OPTIONS_LEN + 1]));
04592          } else if (!strcasecmp(var->name, "maxusers")) {
04593             maxusers = atoi(var->value);
04594          } else if (!strcasecmp(var->name, "adminopts")) {
04595             ast_copy_string(adminopts, var->value, sizeof(char[OPTIONS_LEN + 1]));
04596          } else if (!strcasecmp(var->name, "recordingfilename")) {
04597             ast_copy_string(recordingfilename, var->value, sizeof(recordingfilename));
04598          } else if (!strcasecmp(var->name, "recordingformat")) {
04599             ast_copy_string(recordingformat, var->value, sizeof(recordingformat));
04600          } else if (!strcasecmp(var->name, "endtime")) {
04601             struct ast_tm endtime_tm;
04602             ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
04603             endtime = ast_mktime(&endtime_tm, NULL);
04604          }
04605       }
04606 
04607       ast_variables_destroy(origvar);
04608 
04609       cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan, NULL);
04610 
04611       if (cnf) {
04612          struct ast_flags64 tmp_flags;
04613 
04614          cnf->maxusers = maxusers;
04615          cnf->endalert = endalert;
04616          cnf->endtime = endtime.tv_sec;
04617          cnf->useropts = ast_strdup(useropts);
04618          cnf->adminopts = ast_strdup(adminopts);
04619          cnf->bookid = ast_strdup(bookid);
04620          if (!ast_strlen_zero(recordingfilename)) {
04621             cnf->recordingfilename = ast_strdup(recordingfilename);
04622          }
04623          if (!ast_strlen_zero(recordingformat)) {
04624             cnf->recordingformat = ast_strdup(recordingformat);
04625          }
04626 
04627          /* Parse the other options into confflags -- need to do this in two
04628           * steps, because the parse_options routine zeroes the buffer. */
04629          ast_app_parse_options64(meetme_opts, &tmp_flags, optargs, useropts);
04630          ast_copy_flags64(confflags, &tmp_flags, tmp_flags.flags);
04631 
04632          if (strchr(cnf->useropts, 'r')) {
04633             if (ast_strlen_zero(recordingfilename)) { /* If the recordingfilename in the database is empty, use the channel definition or use the default. */
04634                ast_channel_lock(chan);
04635                if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
04636                   ast_free(cnf->recordingfilename);
04637                   cnf->recordingfilename = ast_strdup(var2);
04638                }
04639                ast_channel_unlock(chan);
04640                if (ast_strlen_zero(cnf->recordingfilename)) {
04641                   snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", cnf->confno, ast_channel_uniqueid(chan));
04642                   ast_free(cnf->recordingfilename);
04643                   cnf->recordingfilename = ast_strdup(recordingtmp);
04644                }
04645             }
04646             if (ast_strlen_zero(cnf->recordingformat)) {/* If the recording format is empty, use the wav as default */
04647                ast_channel_lock(chan);
04648                if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
04649                   ast_free(cnf->recordingformat);
04650                   cnf->recordingformat = ast_strdup(var2);
04651                }
04652                ast_channel_unlock(chan);
04653                if (ast_strlen_zero(cnf->recordingformat)) {
04654                   ast_free(cnf->recordingformat);
04655                   cnf->recordingformat = ast_strdup("wav");
04656                }
04657             }
04658             ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
04659          }
04660       }
04661    }
04662 
04663    if (cnf) {
04664       if (confflags->flags && !cnf->chan &&
04665           !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
04666           ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW) | CONFFLAG_INTROUSER_VMREC) {
04667          ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
04668          ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC);
04669       }
04670 
04671       if (confflags && !cnf->chan &&
04672           ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
04673          ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
04674          ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
04675       }
04676    }
04677 
04678    return cnf;
04679 }
04680 
04681 
04682 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
04683                char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags)
04684 {
04685    struct ast_config *cfg;
04686    struct ast_variable *var;
04687    struct ast_flags config_flags = { 0 };
04688    struct ast_conference *cnf;
04689 
04690    AST_DECLARE_APP_ARGS(args,
04691       AST_APP_ARG(confno);
04692       AST_APP_ARG(pin);
04693       AST_APP_ARG(pinadmin);
04694    );
04695 
04696    /* Check first in the conference list */
04697    ast_debug(1, "The requested confno is '%s'?\n", confno);
04698    AST_LIST_LOCK(&confs);
04699    AST_LIST_TRAVERSE(&confs, cnf, list) {
04700       ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
04701       if (!strcmp(confno, cnf->confno))
04702          break;
04703    }
04704    if (cnf) {
04705       cnf->refcount += refcount;
04706    }
04707    AST_LIST_UNLOCK(&confs);
04708 
04709    if (!cnf) {
04710       if (dynamic) {
04711          /* No need to parse meetme.conf */
04712          ast_debug(1, "Building dynamic conference '%s'\n", confno);
04713          if (dynamic_pin) {
04714             if (dynamic_pin[0] == 'q') {
04715                /* Query the user to enter a PIN */
04716                if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
04717                   return NULL;
04718             }
04719             cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan, NULL);
04720          } else {
04721             cnf = build_conf(confno, "", "", make, dynamic, refcount, chan, NULL);
04722          }
04723       } else {
04724          /* Check the config */
04725          cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04726          if (!cfg) {
04727             ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
04728             return NULL;
04729          } else if (cfg == CONFIG_STATUS_FILEINVALID) {
04730             ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format.  Aborting.\n");
04731             return NULL;
04732          }
04733 
04734          for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
04735             char parse[MAX_SETTINGS];
04736 
04737             if (strcasecmp(var->name, "conf"))
04738                continue;
04739 
04740             ast_copy_string(parse, var->value, sizeof(parse));
04741 
04742             AST_STANDARD_APP_ARGS(args, parse);
04743             ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
04744             if (!strcasecmp(args.confno, confno)) {
04745                /* Bingo it's a valid conference */
04746                cnf = build_conf(args.confno,
04747                      S_OR(args.pin, ""),
04748                      S_OR(args.pinadmin, ""),
04749                      make, dynamic, refcount, chan, NULL);
04750                break;
04751             }
04752          }
04753          if (!var) {
04754             ast_debug(1, "%s isn't a valid conference\n", confno);
04755          }
04756          ast_config_destroy(cfg);
04757       }
04758    } else if (dynamic_pin) {
04759       /* Correct for the user selecting 'D' instead of 'd' to have
04760          someone join into a conference that has already been created
04761          with a pin. */
04762       if (dynamic_pin[0] == 'q') {
04763          dynamic_pin[0] = '\0';
04764       }
04765    }
04766 
04767    if (cnf) {
04768       if (confflags && !cnf->chan &&
04769           !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
04770           ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW  | CONFFLAG_INTROUSER_VMREC)) {
04771          ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
04772          ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC);
04773       }
04774       
04775       if (confflags && !cnf->chan &&
04776           ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
04777          ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
04778          ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
04779       }
04780    }
04781 
04782    return cnf;
04783 }
04784 
04785 /*! \brief The MeetmeCount application */
04786 static int count_exec(struct ast_channel *chan, const char *data)
04787 {
04788    int res = 0;
04789    struct ast_conference *conf;
04790    int count;
04791    char *localdata;
04792    char val[80] = "0"; 
04793    AST_DECLARE_APP_ARGS(args,
04794       AST_APP_ARG(confno);
04795       AST_APP_ARG(varname);
04796    );
04797 
04798    if (ast_strlen_zero(data)) {
04799       ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
04800       return -1;
04801    }
04802    
04803    localdata = ast_strdupa(data);
04804 
04805    AST_STANDARD_APP_ARGS(args, localdata);
04806    
04807    conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
04808 
04809    if (conf) {
04810       count = conf->users;
04811       dispose_conf(conf);
04812       conf = NULL;
04813    } else
04814       count = 0;
04815 
04816    if (!ast_strlen_zero(args.varname)) {
04817       /* have var so load it and exit */
04818       snprintf(val, sizeof(val), "%d", count);
04819       pbx_builtin_setvar_helper(chan, args.varname, val);
04820    } else {
04821       if (ast_channel_state(chan) != AST_STATE_UP) {
04822          ast_answer(chan);
04823       }
04824       res = ast_say_number(chan, count, "", ast_channel_language(chan), (char *) NULL); /* Needs gender */
04825    }
04826 
04827    return res;
04828 }
04829 
04830 /*! \brief The meetme() application */
04831 static int conf_exec(struct ast_channel *chan, const char *data)
04832 {
04833    int res = -1;
04834    char confno[MAX_CONFNUM] = "";
04835    int allowretry = 0;
04836    int retrycnt = 0;
04837    struct ast_conference *cnf = NULL;
04838    struct ast_flags64 confflags = {0};
04839    struct ast_flags config_flags = { 0 };
04840    int dynamic = 0;
04841    int empty = 0, empty_no_pin = 0;
04842    int always_prompt = 0;
04843    const char *notdata;
04844    char *info, the_pin[MAX_PIN] = "";
04845    AST_DECLARE_APP_ARGS(args,
04846       AST_APP_ARG(confno);
04847       AST_APP_ARG(options);
04848       AST_APP_ARG(pin);
04849    );
04850    char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
04851 
04852    if (ast_strlen_zero(data)) {
04853       allowretry = 1;
04854       notdata = "";
04855    } else {
04856       notdata = data;
04857    }
04858    
04859    if (ast_channel_state(chan) != AST_STATE_UP)
04860       ast_answer(chan);
04861 
04862    info = ast_strdupa(notdata);
04863 
04864    AST_STANDARD_APP_ARGS(args, info);  
04865 
04866    if (args.confno) {
04867       ast_copy_string(confno, args.confno, sizeof(confno));
04868       if (ast_strlen_zero(confno)) {
04869          allowretry = 1;
04870       }
04871    }
04872    
04873    if (args.pin)
04874       ast_copy_string(the_pin, args.pin, sizeof(the_pin));
04875 
04876    if (args.options) {
04877       ast_app_parse_options64(meetme_opts, &confflags, optargs, args.options);
04878       dynamic = ast_test_flag64(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
04879       if (ast_test_flag64(&confflags, CONFFLAG_DYNAMICPIN) && ast_strlen_zero(args.pin))
04880          strcpy(the_pin, "q");
04881 
04882       empty = ast_test_flag64(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
04883       empty_no_pin = ast_test_flag64(&confflags, CONFFLAG_EMPTYNOPIN);
04884       always_prompt = ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT | CONFFLAG_DYNAMICPIN);
04885    }
04886 
04887    do {
04888       if (retrycnt > 3)
04889          allowretry = 0;
04890       if (empty) {
04891          int i;
04892          struct ast_config *cfg;
04893          struct ast_variable *var;
04894          int confno_int;
04895 
04896          /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
04897          if ((empty_no_pin) || (!dynamic)) {
04898             cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04899             if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
04900                var = ast_variable_browse(cfg, "rooms");
04901                while (var) {
04902                   char parse[MAX_SETTINGS], *stringp = parse, *confno_tmp;
04903                   if (!strcasecmp(var->name, "conf")) {
04904                      int found = 0;
04905                      ast_copy_string(parse, var->value, sizeof(parse));
04906                      confno_tmp = strsep(&stringp, "|,");
04907                      if (!dynamic) {
04908                         /* For static:  run through the list and see if this conference is empty */
04909                         AST_LIST_LOCK(&confs);
04910                         AST_LIST_TRAVERSE(&confs, cnf, list) {
04911                            if (!strcmp(confno_tmp, cnf->confno)) {
04912                               /* The conference exists, therefore it's not empty */
04913                               found = 1;
04914                               break;
04915                            }
04916                         }
04917                         AST_LIST_UNLOCK(&confs);
04918                         cnf = NULL;
04919                         if (!found) {
04920                            /* At this point, we have a confno_tmp (static conference) that is empty */
04921                            if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
04922                               /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
04923                                * Case 2:  empty_no_pin and pin is blank (but not NULL)
04924                                * Case 3:  not empty_no_pin
04925                                */
04926                               ast_copy_string(confno, confno_tmp, sizeof(confno));
04927                               break;
04928                            }
04929                         }
04930                      }
04931                   }
04932                   var = var->next;
04933                }
04934                ast_config_destroy(cfg);
04935             }
04936 
04937             if (ast_strlen_zero(confno) && (cfg = ast_load_realtime_multientry("meetme", "confno LIKE", "%", SENTINEL))) {
04938                const char *catg;
04939                for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
04940                   const char *confno_tmp = ast_variable_retrieve(cfg, catg, "confno");
04941                   const char *pin_tmp = ast_variable_retrieve(cfg, catg, "pin");
04942                   if (ast_strlen_zero(confno_tmp)) {
04943                      continue;
04944                   }
04945                   if (!dynamic) {
04946                      int found = 0;
04947                      /* For static:  run through the list and see if this conference is empty */
04948                      AST_LIST_LOCK(&confs);
04949                      AST_LIST_TRAVERSE(&confs, cnf, list) {
04950                         if (!strcmp(confno_tmp, cnf->confno)) {
04951                            /* The conference exists, therefore it's not empty */
04952                            found = 1;
04953                            break;
04954                         }
04955                      }
04956                      AST_LIST_UNLOCK(&confs);
04957                      if (!found) {
04958                         /* At this point, we have a confno_tmp (realtime conference) that is empty */
04959                         if ((empty_no_pin && ast_strlen_zero(pin_tmp)) || (!empty_no_pin)) {
04960                            /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
04961                             * Case 2:  empty_no_pin and pin is blank (but not NULL)
04962                             * Case 3:  not empty_no_pin
04963                             */
04964                            ast_copy_string(confno, confno_tmp, sizeof(confno));
04965                            break;
04966                         }
04967                      }
04968                   }
04969                }
04970                ast_config_destroy(cfg);
04971             }
04972          }
04973 
04974          /* Select first conference number not in use */
04975          if (ast_strlen_zero(confno) && dynamic) {
04976             AST_LIST_LOCK(&confs);
04977             for (i = 0; i < ARRAY_LEN(conf_map); i++) {
04978                if (!conf_map[i]) {
04979                   snprintf(confno, sizeof(confno), "%d", i);
04980                   conf_map[i] = 1;
04981                   break;
04982                }
04983             }
04984             AST_LIST_UNLOCK(&confs);
04985          }
04986 
04987          /* Not found? */
04988          if (ast_strlen_zero(confno)) {
04989             res = ast_streamfile(chan, "conf-noempty", ast_channel_language(chan));
04990             ast_test_suite_event_notify("PLAYBACK", "Message: conf-noempty");
04991             if (!res)
04992                ast_waitstream(chan, "");
04993          } else {
04994             if (sscanf(confno, "%30d", &confno_int) == 1) {
04995                if (!ast_test_flag64(&confflags, CONFFLAG_QUIET)) {
04996                   res = ast_streamfile(chan, "conf-enteringno", ast_channel_language(chan));
04997                   if (!res) {
04998                      ast_waitstream(chan, "");
04999                      res = ast_say_digits(chan, confno_int, "", ast_channel_language(chan));
05000                   }
05001                }
05002             } else {
05003                ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
05004             }
05005          }
05006       }
05007 
05008       while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
05009          /* Prompt user for conference number */
05010          res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
05011          if (res < 0) {
05012             /* Don't try to validate when we catch an error */
05013             confno[0] = '\0';
05014             allowretry = 0;
05015             break;
05016          }
05017       }
05018       if (!ast_strlen_zero(confno)) {
05019          /* Check the validity of the conference */
05020          cnf = find_conf(chan, confno, 1, dynamic, the_pin, 
05021             sizeof(the_pin), 1, &confflags);
05022          if (!cnf) {
05023             int too_early = 0;
05024 
05025             cnf = find_conf_realtime(chan, confno, 1, dynamic, 
05026                the_pin, sizeof(the_pin), 1, &confflags, &too_early, optargs);
05027             if (rt_schedule && too_early)
05028                allowretry = 0;
05029          }
05030 
05031          if (!cnf) {
05032             if (allowretry) {
05033                confno[0] = '\0';
05034                res = ast_streamfile(chan, "conf-invalid", ast_channel_language(chan));
05035                if (!res)
05036                   ast_waitstream(chan, "");
05037                res = -1;
05038             }
05039          } else {
05040             /* Conference requires a pin for specified access level */
05041             int req_pin = !ast_strlen_zero(cnf->pin) ||
05042                (!ast_strlen_zero(cnf->pinadmin) &&
05043                   ast_test_flag64(&confflags, CONFFLAG_ADMIN));
05044             /* The following logic was derived from a
05045              * 4 variable truth table and defines which
05046              * circumstances are not exempt from pin
05047              * checking.
05048              * If this needs to be modified, write the
05049              * truth table back out from the boolean
05050              * expression AB+A'D+C', change the erroneous
05051              * result, and rederive the expression.
05052              * Variables:
05053              *  A: pin provided?
05054              *  B: always prompt?
05055              *  C: dynamic?
05056              *  D: has users? */
05057             int not_exempt = !cnf->isdynamic;
05058             not_exempt = not_exempt || (!ast_strlen_zero(args.pin) && ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT));
05059             not_exempt = not_exempt || (ast_strlen_zero(args.pin) && cnf->users);
05060             if (req_pin && not_exempt) {
05061                char pin[MAX_PIN] = "";
05062                int j;
05063 
05064                /* Allow the pin to be retried up to 3 times */
05065                for (j = 0; j < 3; j++) {
05066                   if (*the_pin && (always_prompt == 0)) {
05067                      ast_copy_string(pin, the_pin, sizeof(pin));
05068                      res = 0;
05069                   } else {
05070                      /* Prompt user for pin if pin is required */
05071                      ast_test_suite_event_notify("PLAYBACK", "Message: conf-getpin\r\n"
05072                         "Channel: %s",
05073                         ast_channel_name(chan));
05074                      res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
05075                   }
05076                   if (res >= 0) {
05077                      if ((!strcasecmp(pin, cnf->pin) &&
05078                           (ast_strlen_zero(cnf->pinadmin) ||
05079                            !ast_test_flag64(&confflags, CONFFLAG_ADMIN))) ||
05080                           (!ast_strlen_zero(cnf->pinadmin) &&
05081                            !strcasecmp(pin, cnf->pinadmin))) {
05082                         /* Pin correct */
05083                         allowretry = 0;
05084                         if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) {
05085                            if (!ast_strlen_zero(cnf->adminopts)) {
05086                               char *opts = ast_strdupa(cnf->adminopts);
05087                               ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
05088                            }
05089                         } else {
05090                            if (!ast_strlen_zero(cnf->useropts)) {
05091                               char *opts = ast_strdupa(cnf->useropts);
05092                               ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
05093                            }
05094                         }
05095                         /* Run the conference */
05096                         ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
05097                         res = conf_run(chan, cnf, &confflags, optargs);
05098                         break;
05099                      } else {
05100                         /* Pin invalid */
05101                         if (!ast_streamfile(chan, "conf-invalidpin", ast_channel_language(chan))) {
05102                            res = ast_waitstream(chan, AST_DIGIT_ANY);
05103                            ast_stopstream(chan);
05104                         } else {
05105                            ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
05106                            break;
05107                         }
05108                         if (res < 0)
05109                            break;
05110                         pin[0] = res;
05111                         pin[1] = '\0';
05112                         res = -1;
05113                         if (allowretry)
05114                            confno[0] = '\0';
05115                      }
05116                   } else {
05117                      /* failed when getting the pin */
05118                      res = -1;
05119                      allowretry = 0;
05120                      /* see if we need to get rid of the conference */
05121                      break;
05122                   }
05123 
05124                   /* Don't retry pin with a static pin */
05125                   if (*the_pin && (always_prompt == 0)) {
05126                      break;
05127                   }
05128                }
05129             } else {
05130                /* No pin required */
05131                allowretry = 0;
05132 
05133                /* For RealTime conferences without a pin 
05134                 * should still support loading options
05135                 */
05136                if (!ast_strlen_zero(cnf->useropts)) {
05137                   char *opts = ast_strdupa(cnf->useropts);
05138                   ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
05139                }
05140 
05141                /* Run the conference */
05142                res = conf_run(chan, cnf, &confflags, optargs);
05143             }
05144             dispose_conf(cnf);
05145             cnf = NULL;
05146          }
05147       }
05148    } while (allowretry);
05149 
05150    if (cnf)
05151       dispose_conf(cnf);
05152    
05153    return res;
05154 }
05155 
05156 static struct ast_conf_user *find_user(struct ast_conference *conf, const char *callerident)
05157 {
05158    struct ast_conf_user *user = NULL;
05159    int cid;
05160 
05161    if (conf && callerident && sscanf(callerident, "%30d", &cid) == 1) {
05162       user = ao2_find(conf->usercontainer, &cid, 0);
05163       /* reference decremented later in admin_exec */
05164       return user;
05165    }
05166    return NULL;
05167 }
05168 
05169 static int user_listen_volup_cb(void *obj, void *unused, int flags)
05170 {
05171    struct ast_conf_user *user = obj;
05172    tweak_listen_volume(user, VOL_UP);
05173    return 0;
05174 }
05175 
05176 static int user_listen_voldown_cb(void *obj, void *unused, int flags)
05177 {
05178    struct ast_conf_user *user = obj;
05179    tweak_listen_volume(user, VOL_DOWN);
05180    return 0;
05181 }
05182 
05183 static int user_talk_volup_cb(void *obj, void *unused, int flags)
05184 {
05185    struct ast_conf_user *user = obj;
05186    tweak_talk_volume(user, VOL_UP);
05187    return 0;
05188 }
05189 
05190 static int user_talk_voldown_cb(void *obj, void *unused, int flags)
05191 {
05192    struct ast_conf_user *user = obj;
05193    tweak_talk_volume(user, VOL_DOWN);
05194    return 0;
05195 }
05196 
05197 static int user_reset_vol_cb(void *obj, void *unused, int flags)
05198 {
05199    struct ast_conf_user *user = obj;
05200    reset_volumes(user);
05201    return 0;
05202 }
05203 
05204 static int user_chan_cb(void *obj, void *args, int flags)
05205 {
05206    struct ast_conf_user *user = obj;
05207    const char *channel = args;
05208 
05209    if (!strcmp(ast_channel_name(user->chan), channel)) {
05210       return (CMP_MATCH | CMP_STOP);
05211    }
05212 
05213    return 0;
05214 }
05215 
05216 /*! \brief The MeetMeadmin application 
05217 
05218   MeetMeAdmin(confno, command, caller) */
05219 static int admin_exec(struct ast_channel *chan, const char *data) {
05220    char *params;
05221    struct ast_conference *cnf;
05222    struct ast_conf_user *user = NULL;
05223    AST_DECLARE_APP_ARGS(args,
05224       AST_APP_ARG(confno);
05225       AST_APP_ARG(command);
05226       AST_APP_ARG(user);
05227    );
05228    int res = 0;
05229 
05230    if (ast_strlen_zero(data)) {
05231       ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
05232       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
05233       return -1;
05234    }
05235 
05236    params = ast_strdupa(data);
05237    AST_STANDARD_APP_ARGS(args, params);
05238 
05239    if (!args.command) {
05240       ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
05241       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
05242       return -1;
05243    }
05244 
05245    AST_LIST_LOCK(&confs);
05246    AST_LIST_TRAVERSE(&confs, cnf, list) {
05247       if (!strcmp(cnf->confno, args.confno))
05248          break;
05249    }
05250 
05251    if (!cnf) {
05252       ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
05253       AST_LIST_UNLOCK(&confs);
05254       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOTFOUND");
05255       return 0;
05256    }
05257 
05258    ast_atomic_fetchadd_int(&cnf->refcount, 1);
05259 
05260    if (args.user) {
05261       user = find_user(cnf, args.user);
05262       if (!user) {
05263          ast_log(LOG_NOTICE, "Specified User not found!\n");
05264          res = -2;
05265          goto usernotfound;
05266       }
05267    } else {
05268       /* fail for commands that require a user */
05269       switch (*args.command) {
05270       case 'm': /* Unmute */
05271       case 'M': /* Mute */
05272       case 't': /* Lower user's talk volume */
05273       case 'T': /* Raise user's talk volume */
05274       case 'u': /* Lower user's listen volume */
05275       case 'U': /* Raise user's listen volume */
05276       case 'r': /* Reset user's volume level */
05277       case 'k': /* Kick user */
05278          res = -2;
05279          ast_log(LOG_NOTICE, "No user specified!\n");
05280          goto usernotfound;
05281       default:
05282          break;
05283       }
05284    }
05285 
05286    switch (*args.command) {
05287    case 76: /* L: Lock */ 
05288       cnf->locked = 1;
05289       break;
05290    case 108: /* l: Unlock */ 
05291       cnf->locked = 0;
05292       break;
05293    case 75: /* K: kick all users */
05294       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_kickme_cb, NULL);
05295       break;
05296    case 101: /* e: Eject last user*/
05297    {
05298       int max_no = 0;
05299       RAII_VAR(struct ast_conf_user *, eject_user, NULL, ao2_cleanup);
05300 
05301       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
05302       eject_user = ao2_find(cnf->usercontainer, &max_no, 0);
05303       if (!eject_user) {
05304          res = -1;
05305          ast_log(LOG_NOTICE, "No last user to kick!\n");
05306          break;
05307       }
05308 
05309       if (!ast_test_flag64(&eject_user->userflags, CONFFLAG_ADMIN)) {
05310          eject_user->adminflags |= ADMINFLAG_KICKME;
05311       } else {
05312          res = -1;
05313          ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
05314       }
05315       break;
05316    }
05317    case 77: /* M: Mute */ 
05318       user->adminflags |= ADMINFLAG_MUTED;
05319       break;
05320    case 78: /* N: Mute all (non-admin) users */
05321       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_muted_cb, &cnf);
05322       break;               
05323    case 109: /* m: Unmute */ 
05324       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
05325       break;
05326    case 110: /* n: Unmute all users */
05327       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, NULL);
05328       break;
05329    case 107: /* k: Kick user */ 
05330       user->adminflags |= ADMINFLAG_KICKME;
05331       break;
05332    case 118: /* v: Lower all users listen volume */
05333       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_voldown_cb, NULL);
05334       break;
05335    case 86: /* V: Raise all users listen volume */
05336       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_volup_cb, NULL);
05337       break;
05338    case 115: /* s: Lower all users speaking volume */
05339       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_voldown_cb, NULL);
05340       break;
05341    case 83: /* S: Raise all users speaking volume */
05342       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_volup_cb, NULL);
05343       break;
05344    case 82: /* R: Reset all volume levels */
05345       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_reset_vol_cb, NULL);
05346       break;
05347    case 114: /* r: Reset user's volume level */
05348       reset_volumes(user);
05349       break;
05350    case 85: /* U: Raise user's listen volume */
05351       tweak_listen_volume(user, VOL_UP);
05352       break;
05353    case 117: /* u: Lower user's listen volume */
05354       tweak_listen_volume(user, VOL_DOWN);
05355       break;
05356    case 84: /* T: Raise user's talk volume */
05357       tweak_talk_volume(user, VOL_UP);
05358       break;
05359    case 116: /* t: Lower user's talk volume */
05360       tweak_talk_volume(user, VOL_DOWN);
05361       break;
05362    case 'E': /* E: Extend conference */
05363       if (rt_extend_conf(args.confno)) {
05364          res = -1;
05365       }
05366       break;
05367    }
05368 
05369    if (args.user) {
05370       /* decrement reference from find_user */
05371       ao2_ref(user, -1);
05372    }
05373 usernotfound:
05374    AST_LIST_UNLOCK(&confs);
05375 
05376    dispose_conf(cnf);
05377    pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", res == -2 ? "NOTFOUND" : res ? "FAILED" : "OK");
05378 
05379    return 0;
05380 }
05381 
05382 /*! \brief The MeetMeChannelAdmin application 
05383    MeetMeChannelAdmin(channel, command) */
05384 static int channel_admin_exec(struct ast_channel *chan, const char *data) {
05385    char *params;
05386    struct ast_conference *conf = NULL;
05387    struct ast_conf_user *user = NULL;
05388    AST_DECLARE_APP_ARGS(args,
05389       AST_APP_ARG(channel);
05390       AST_APP_ARG(command);
05391    );
05392 
05393    if (ast_strlen_zero(data)) {
05394       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
05395       return -1;
05396    }
05397    
05398    params = ast_strdupa(data);
05399    AST_STANDARD_APP_ARGS(args, params);
05400 
05401    if (!args.channel) {
05402       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
05403       return -1;
05404    }
05405 
05406    if (!args.command) {
05407       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
05408       return -1;
05409    }
05410 
05411    AST_LIST_LOCK(&confs);
05412    AST_LIST_TRAVERSE(&confs, conf, list) {
05413       if ((user = ao2_callback(conf->usercontainer, 0, user_chan_cb, args.channel))) {
05414          break;
05415       }
05416    }
05417    
05418    if (!user) {
05419       ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
05420       AST_LIST_UNLOCK(&confs);
05421       return 0;
05422    }
05423    
05424    /* perform the specified action */
05425    switch (*args.command) {
05426       case 77: /* M: Mute */ 
05427          user->adminflags |= ADMINFLAG_MUTED;
05428          break;
05429       case 109: /* m: Unmute */ 
05430          user->adminflags &= ~ADMINFLAG_MUTED;
05431          break;
05432       case 107: /* k: Kick user */ 
05433          user->adminflags |= ADMINFLAG_KICKME;
05434          break;
05435       default: /* unknown command */
05436          ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
05437          break;
05438    }
05439    ao2_ref(user, -1);
05440    AST_LIST_UNLOCK(&confs);
05441    
05442    return 0;
05443 }
05444 
05445 static int meetmemute(struct mansession *s, const struct message *m, int mute)
05446 {
05447    struct ast_conference *conf;
05448    struct ast_conf_user *user;
05449    const char *confid = astman_get_header(m, "Meetme");
05450    char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
05451    int userno;
05452 
05453    if (ast_strlen_zero(confid)) {
05454       astman_send_error(s, m, "Meetme conference not specified");
05455       return 0;
05456    }
05457 
05458    if (ast_strlen_zero(userid)) {
05459       astman_send_error(s, m, "Meetme user number not specified");
05460       return 0;
05461    }
05462 
05463    userno = strtoul(userid, &userid, 10);
05464 
05465    if (*userid) {
05466       astman_send_error(s, m, "Invalid user number");
05467       return 0;
05468    }
05469 
05470    /* Look in the conference list */
05471    AST_LIST_LOCK(&confs);
05472    AST_LIST_TRAVERSE(&confs, conf, list) {
05473       if (!strcmp(confid, conf->confno))
05474          break;
05475    }
05476 
05477    if (!conf) {
05478       AST_LIST_UNLOCK(&confs);
05479       astman_send_error(s, m, "Meetme conference does not exist");
05480       return 0;
05481    }
05482 
05483    user = ao2_find(conf->usercontainer, &userno, 0);
05484 
05485    if (!user) {
05486       AST_LIST_UNLOCK(&confs);
05487       astman_send_error(s, m, "User number not found");
05488       return 0;
05489    }
05490 
05491    if (mute)
05492       user->adminflags |= ADMINFLAG_MUTED;   /* request user muting */
05493    else
05494       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST); /* request user unmuting */
05495 
05496    AST_LIST_UNLOCK(&confs);
05497 
05498    ast_log(LOG_NOTICE, "Requested to %smute conf %s user %d userchan %s uniqueid %s\n", mute ? "" : "un", conf->confno, user->user_no, ast_channel_name(user->chan), ast_channel_uniqueid(user->chan));
05499 
05500    ao2_ref(user, -1);
05501    astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
05502    return 0;
05503 }
05504 
05505 static int action_meetmemute(struct mansession *s, const struct message *m)
05506 {
05507    return meetmemute(s, m, 1);
05508 }
05509 
05510 static int action_meetmeunmute(struct mansession *s, const struct message *m)
05511 {
05512    return meetmemute(s, m, 0);
05513 }
05514 
05515 static int action_meetmelist(struct mansession *s, const struct message *m)
05516 {
05517    const char *actionid = astman_get_header(m, "ActionID");
05518    const char *conference = astman_get_header(m, "Conference");
05519    char idText[80] = "";
05520    struct ast_conference *cnf;
05521    struct ast_conf_user *user;
05522    struct ao2_iterator user_iter;
05523    int total = 0;
05524 
05525    if (!ast_strlen_zero(actionid))
05526       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
05527 
05528    if (AST_LIST_EMPTY(&confs)) {
05529       astman_send_error(s, m, "No active conferences.");
05530       return 0;
05531    }
05532 
05533    astman_send_listack(s, m, "Meetme user list will follow", "start");
05534 
05535    /* Find the right conference */
05536    AST_LIST_LOCK(&confs);
05537    AST_LIST_TRAVERSE(&confs, cnf, list) {
05538       /* If we ask for one particular, and this isn't it, skip it */
05539       if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
05540          continue;
05541 
05542       /* Show all the users */
05543       user_iter = ao2_iterator_init(cnf->usercontainer, 0);
05544       while ((user = ao2_iterator_next(&user_iter))) {
05545          total++;
05546          astman_append(s,
05547             "Event: MeetmeList\r\n"
05548             "%s"
05549             "Conference: %s\r\n"
05550             "UserNumber: %d\r\n"
05551             "CallerIDNum: %s\r\n"
05552             "CallerIDName: %s\r\n"
05553             "ConnectedLineNum: %s\r\n"
05554             "ConnectedLineName: %s\r\n"
05555             "Channel: %s\r\n"
05556             "Admin: %s\r\n"
05557             "Role: %s\r\n"
05558             "MarkedUser: %s\r\n"
05559             "Muted: %s\r\n"
05560             "Talking: %s\r\n"
05561             "\r\n",
05562             idText,
05563             cnf->confno,
05564             user->user_no,
05565             S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
05566             S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
05567             S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, "<unknown>"),
05568             S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "<no name>"),
05569             ast_channel_name(user->chan),
05570             ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "Yes" : "No",
05571             ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "Listen only" : ast_test_flag64(&user->userflags, CONFFLAG_TALKER) ? "Talk only" : "Talk and listen",
05572             ast_test_flag64(&user->userflags, CONFFLAG_MARKEDUSER) ? "Yes" : "No",
05573             user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
05574             user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
05575          ao2_ref(user, -1);
05576       }
05577       ao2_iterator_destroy(&user_iter);
05578    }
05579    AST_LIST_UNLOCK(&confs);
05580 
05581    /* Send final confirmation */
05582    astman_send_list_complete_start(s, m, "MeetmeListComplete", total);
05583    astman_send_list_complete_end(s);
05584    return 0;
05585 }
05586 
05587 static int action_meetmelistrooms(struct mansession *s, const struct message *m)
05588 {
05589    const char *actionid = astman_get_header(m, "ActionID");
05590    char idText[80] = "";
05591    struct ast_conference *cnf;
05592    int totalitems = 0;
05593    int hr, min, sec;
05594    time_t now;
05595    char markedusers[5];
05596 
05597    if (!ast_strlen_zero(actionid)) {
05598       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
05599    }
05600 
05601    if (AST_LIST_EMPTY(&confs)) {
05602       astman_send_error(s, m, "No active conferences.");
05603       return 0;
05604    }
05605 
05606    astman_send_listack(s, m, "Meetme conferences will follow", "start");
05607 
05608    now = time(NULL);
05609 
05610    /* Traverse the conference list */
05611    AST_LIST_LOCK(&confs);
05612    AST_LIST_TRAVERSE(&confs, cnf, list) {
05613       totalitems++;
05614 
05615       if (cnf->markedusers == 0) {
05616          strcpy(markedusers, "N/A");
05617       } else {
05618          sprintf(markedusers, "%.4d", cnf->markedusers);
05619       }
05620       hr = (now - cnf->start) / 3600;
05621       min = ((now - cnf->start) % 3600) / 60;
05622       sec = (now - cnf->start) % 60;
05623 
05624       astman_append(s,
05625       "Event: MeetmeListRooms\r\n"
05626       "%s"
05627       "Conference: %s\r\n"
05628       "Parties: %d\r\n"
05629       "Marked: %s\r\n"
05630       "Activity: %2.2d:%2.2d:%2.2d\r\n"
05631       "Creation: %s\r\n"
05632       "Locked: %s\r\n"
05633       "\r\n",
05634       idText,
05635       cnf->confno,
05636       cnf->users,
05637       markedusers,
05638       hr,  min, sec,
05639       cnf->isdynamic ? "Dynamic" : "Static",
05640       cnf->locked ? "Yes" : "No"); 
05641    }
05642    AST_LIST_UNLOCK(&confs);
05643 
05644    /* Send final confirmation */
05645    astman_send_list_complete_start(s, m, "MeetmeListRoomsComplete", totalitems);
05646    astman_send_list_complete_end(s);
05647    return 0;
05648 }
05649 
05650 /*! \internal
05651  * \brief creates directory structure and assigns absolute path from relative paths for filenames
05652  *
05653  * \param filename contains the absolute or relative path to the desired file
05654  * \param buffer stores completed filename, absolutely must be a buffer of PATH_MAX length
05655  */
05656 static void filename_parse(char *filename, char *buffer)
05657 {
05658    char *slash;
05659    if (ast_strlen_zero(filename)) {
05660       ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
05661    } else if (filename[0] != '/') {
05662       snprintf(buffer, PATH_MAX, "%s/meetme/%s", ast_config_AST_SPOOL_DIR, filename);
05663    } else {
05664       ast_copy_string(buffer, filename, PATH_MAX);
05665    }
05666 
05667    slash = buffer;
05668    if ((slash = strrchr(slash, '/'))) {
05669       *slash = '\0';
05670       ast_mkdir(buffer, 0777);
05671       *slash = '/';
05672    }
05673 }
05674 
05675 static void *recordthread(void *args)
05676 {
05677    struct ast_conference *cnf = args;
05678    struct ast_frame *f = NULL;
05679    int flags;
05680    struct ast_filestream *s = NULL;
05681    int res = 0;
05682    int x;
05683    const char *oldrecordingfilename = NULL;
05684    char filename_buffer[PATH_MAX];
05685 
05686    if (!cnf || !cnf->lchan) {
05687       pthread_exit(0);
05688    }
05689 
05690    filename_buffer[0] = '\0';
05691    filename_parse(cnf->recordingfilename, filename_buffer);
05692 
05693    ast_stopstream(cnf->lchan);
05694    flags = O_CREAT | O_TRUNC | O_WRONLY;
05695 
05696 
05697    cnf->recording = MEETME_RECORD_ACTIVE;
05698    while (ast_waitfor(cnf->lchan, -1) > -1) {
05699       if (cnf->recording == MEETME_RECORD_TERMINATE) {
05700          AST_LIST_LOCK(&confs);
05701          AST_LIST_UNLOCK(&confs);
05702          break;
05703       }
05704       if (!s && !(ast_strlen_zero(filename_buffer)) && (filename_buffer != oldrecordingfilename)) {
05705          s = ast_writefile(filename_buffer, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
05706          oldrecordingfilename = filename_buffer;
05707       }
05708       
05709       f = ast_read(cnf->lchan);
05710       if (!f) {
05711          res = -1;
05712          break;
05713       }
05714       if (f->frametype == AST_FRAME_VOICE) {
05715          ast_mutex_lock(&cnf->listenlock);
05716          for (x = 0; x < AST_FRAME_BITS; x++) {
05717             /* Free any translations that have occured */
05718             if (cnf->transframe[x]) {
05719                ast_frfree(cnf->transframe[x]);
05720                cnf->transframe[x] = NULL;
05721             }
05722          }
05723          if (cnf->origframe)
05724             ast_frfree(cnf->origframe);
05725          cnf->origframe = ast_frdup(f);
05726          ast_mutex_unlock(&cnf->listenlock);
05727          if (s)
05728             res = ast_writestream(s, f);
05729          if (res) {
05730             ast_frfree(f);
05731             break;
05732          }
05733       }
05734       ast_frfree(f);
05735    }
05736    cnf->recording = MEETME_RECORD_OFF;
05737    if (s)
05738       ast_closestream(s);
05739    
05740    pthread_exit(0);
05741 }
05742 
05743 /*! \brief Callback for devicestate providers */
05744 static enum ast_device_state meetmestate(const char *data)
05745 {
05746    struct ast_conference *conf;
05747 
05748    /* Find conference */
05749    AST_LIST_LOCK(&confs);
05750    AST_LIST_TRAVERSE(&confs, conf, list) {
05751       if (!strcmp(data, conf->confno))
05752          break;
05753    }
05754    AST_LIST_UNLOCK(&confs);
05755    if (!conf)
05756       return AST_DEVICE_INVALID;
05757 
05758 
05759    /* SKREP to fill */
05760    if (!conf->users)
05761       return AST_DEVICE_NOT_INUSE;
05762 
05763    return AST_DEVICE_INUSE;
05764 }
05765 
05766 static void meetme_set_defaults(void)
05767 {
05768    /*  Scheduling support is off by default */
05769    rt_schedule = 0;
05770    fuzzystart = 0;
05771    earlyalert = 0;
05772    endalert = 0;
05773    extendby = 0;
05774 
05775    /*  Logging of participants defaults to ON for compatibility reasons */
05776    rt_log_members = 1;
05777 }
05778 
05779 static void load_config_meetme(int reload)
05780 {
05781    struct ast_config *cfg;
05782    struct ast_flags config_flags = { 0 };
05783    const char *val;
05784 
05785    if (!reload) {
05786       meetme_set_defaults();
05787    }
05788 
05789    if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags))) {
05790       return;
05791    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
05792       ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format.  Aborting.\n");
05793       return;
05794    }
05795 
05796    if (reload) {
05797       meetme_set_defaults();
05798    }
05799 
05800    if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
05801       if ((sscanf(val, "%30d", &audio_buffers) != 1)) {
05802          ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
05803          audio_buffers = DEFAULT_AUDIO_BUFFERS;
05804       } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
05805          ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
05806             DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
05807          audio_buffers = DEFAULT_AUDIO_BUFFERS;
05808       }
05809       if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
05810          ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
05811    }
05812 
05813    if ((val = ast_variable_retrieve(cfg, "general", "schedule")))
05814       rt_schedule = ast_true(val);
05815    if ((val = ast_variable_retrieve(cfg, "general", "logmembercount")))
05816       rt_log_members = ast_true(val);
05817    if ((val = ast_variable_retrieve(cfg, "general", "fuzzystart"))) {
05818       if ((sscanf(val, "%30d", &fuzzystart) != 1)) {
05819          ast_log(LOG_WARNING, "fuzzystart must be a number, not '%s'\n", val);
05820          fuzzystart = 0;
05821       } 
05822    }
05823    if ((val = ast_variable_retrieve(cfg, "general", "earlyalert"))) {
05824       if ((sscanf(val, "%30d", &earlyalert) != 1)) {
05825          ast_log(LOG_WARNING, "earlyalert must be a number, not '%s'\n", val);
05826          earlyalert = 0;
05827       } 
05828    }
05829    if ((val = ast_variable_retrieve(cfg, "general", "endalert"))) {
05830       if ((sscanf(val, "%30d", &endalert) != 1)) {
05831          ast_log(LOG_WARNING, "endalert must be a number, not '%s'\n", val);
05832          endalert = 0;
05833       } 
05834    }
05835    if ((val = ast_variable_retrieve(cfg, "general", "extendby"))) {
05836       if ((sscanf(val, "%30d", &extendby) != 1)) {
05837          ast_log(LOG_WARNING, "extendby must be a number, not '%s'\n", val);
05838          extendby = 0;
05839       } 
05840    }
05841 
05842    ast_config_destroy(cfg);
05843 }
05844 
05845 /*!
05846  * \internal
05847  * \brief Find an SLA trunk by name
05848  */
05849 static struct sla_trunk *sla_find_trunk(const char *name)
05850 {
05851    struct sla_trunk tmp_trunk = {
05852       .name = name,
05853    };
05854 
05855    return ao2_find(sla_trunks, &tmp_trunk, OBJ_POINTER);
05856 }
05857 
05858 /*!
05859  * \internal
05860  * \brief Find an SLA station by name
05861  */
05862 static struct sla_station *sla_find_station(const char *name)
05863 {
05864    struct sla_station tmp_station = {
05865       .name = name,
05866    };
05867 
05868    return ao2_find(sla_stations, &tmp_station, OBJ_POINTER);
05869 }
05870 
05871 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
05872    const struct sla_station *station)
05873 {
05874    struct sla_station_ref *station_ref;
05875    struct sla_trunk_ref *trunk_ref;
05876 
05877    /* For each station that has this call on hold, check for private hold. */
05878    AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
05879       AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
05880          if (trunk_ref->trunk != trunk || station_ref->station == station)
05881             continue;
05882          if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
05883             station_ref->station->hold_access == SLA_HOLD_PRIVATE)
05884             return 1;
05885          return 0;
05886       }
05887    }
05888 
05889    return 0;
05890 }
05891 
05892 /*!
05893  * \brief Find a trunk reference on a station by name
05894  * \param station the station
05895  * \param name the trunk's name
05896  * \pre sla_station is locked
05897  * \return a pointer to the station's trunk reference.  If the trunk
05898  *         is not found, it is not idle and barge is disabled, or if
05899  *         it is on hold and private hold is set, then NULL will be returned.
05900  */
05901 static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
05902    const char *name)
05903 {
05904    struct sla_trunk_ref *trunk_ref = NULL;
05905 
05906    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05907       if (strcasecmp(trunk_ref->trunk->name, name))
05908          continue;
05909 
05910       if ( (trunk_ref->trunk->barge_disabled 
05911          && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
05912          (trunk_ref->trunk->hold_stations 
05913          && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
05914          && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
05915          sla_check_station_hold_access(trunk_ref->trunk, station) ) 
05916       {
05917          trunk_ref = NULL;
05918       }
05919 
05920       break;
05921    }
05922 
05923    if (trunk_ref) {
05924       ao2_ref(trunk_ref, 1);
05925    }
05926 
05927    return trunk_ref;
05928 }
05929 
05930 static void sla_station_ref_destructor(void *obj)
05931 {
05932    struct sla_station_ref *station_ref = obj;
05933 
05934    if (station_ref->station) {
05935       ao2_ref(station_ref->station, -1);
05936       station_ref->station = NULL;
05937    }
05938 }
05939 
05940 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
05941 {
05942    struct sla_station_ref *station_ref;
05943 
05944    if (!(station_ref = ao2_alloc(sizeof(*station_ref), sla_station_ref_destructor))) {
05945       return NULL;
05946    }
05947 
05948    ao2_ref(station, 1);
05949    station_ref->station = station;
05950 
05951    return station_ref;
05952 }
05953 
05954 static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
05955 {
05956    struct sla_ringing_station *ringing_station;
05957 
05958    if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
05959       return NULL;
05960 
05961    ao2_ref(station, 1);
05962    ringing_station->station = station;
05963    ringing_station->ring_begin = ast_tvnow();
05964 
05965    return ringing_station;
05966 }
05967 
05968 static void sla_ringing_station_destroy(struct sla_ringing_station *ringing_station)
05969 {
05970    if (ringing_station->station) {
05971       ao2_ref(ringing_station->station, -1);
05972       ringing_station->station = NULL;
05973    }
05974 
05975    ast_free(ringing_station);
05976 }
05977 
05978 static struct sla_failed_station *sla_create_failed_station(struct sla_station *station)
05979 {
05980    struct sla_failed_station *failed_station;
05981 
05982    if (!(failed_station = ast_calloc(1, sizeof(*failed_station)))) {
05983       return NULL;
05984    }
05985 
05986    ao2_ref(station, 1);
05987    failed_station->station = station;
05988    failed_station->last_try = ast_tvnow();
05989 
05990    return failed_station;
05991 }
05992 
05993 static void sla_failed_station_destroy(struct sla_failed_station *failed_station)
05994 {
05995    if (failed_station->station) {
05996       ao2_ref(failed_station->station, -1);
05997       failed_station->station = NULL;
05998    }
05999 
06000    ast_free(failed_station);
06001 }
06002 
06003 static enum ast_device_state sla_state_to_devstate(enum sla_trunk_state state)
06004 {
06005    switch (state) {
06006    case SLA_TRUNK_STATE_IDLE:
06007       return AST_DEVICE_NOT_INUSE;
06008    case SLA_TRUNK_STATE_RINGING:
06009       return AST_DEVICE_RINGING;
06010    case SLA_TRUNK_STATE_UP:
06011       return AST_DEVICE_INUSE;
06012    case SLA_TRUNK_STATE_ONHOLD:
06013    case SLA_TRUNK_STATE_ONHOLD_BYME:
06014       return AST_DEVICE_ONHOLD;
06015    }
06016 
06017    return AST_DEVICE_UNKNOWN;
06018 }
06019 
06020 static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state, 
06021    enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
06022 {
06023    struct sla_station *station;
06024    struct sla_trunk_ref *trunk_ref;
06025    struct ao2_iterator i;
06026 
06027    i = ao2_iterator_init(sla_stations, 0);
06028    while ((station = ao2_iterator_next(&i))) {
06029       ao2_lock(station);
06030       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06031          if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
06032                || trunk_ref == exclude) {
06033             continue;
06034          }
06035          trunk_ref->state = state;
06036          ast_devstate_changed(sla_state_to_devstate(state), AST_DEVSTATE_CACHABLE,
06037                     "SLA:%s_%s", station->name, trunk->name);
06038          break;
06039       }
06040       ao2_unlock(station);
06041       ao2_ref(station, -1);
06042    }
06043    ao2_iterator_destroy(&i);
06044 }
06045 
06046 struct run_station_args {
06047    struct sla_station *station;
06048    struct sla_trunk_ref *trunk_ref;
06049    ast_mutex_t *cond_lock;
06050    ast_cond_t *cond;
06051 };
06052 
06053 static void answer_trunk_chan(struct ast_channel *chan)
06054 {
06055    ast_answer(chan);
06056    ast_indicate(chan, -1);
06057 }
06058 
06059 static void *run_station(void *data)
06060 {
06061    RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
06062    RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
06063    struct ast_str *conf_name = ast_str_create(16);
06064    struct ast_flags64 conf_flags = { 0 };
06065    struct ast_conference *conf;
06066 
06067    {
06068       struct run_station_args *args = data;
06069       station = args->station;
06070       trunk_ref = args->trunk_ref;
06071       ast_mutex_lock(args->cond_lock);
06072       ast_cond_signal(args->cond);
06073       ast_mutex_unlock(args->cond_lock);
06074       /* args is no longer valid here. */
06075    }
06076 
06077    ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
06078    ast_str_set(&conf_name, 0, "SLA_%s", trunk_ref->trunk->name);
06079    ast_set_flag64(&conf_flags, 
06080       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
06081    answer_trunk_chan(trunk_ref->chan);
06082    conf = build_conf(ast_str_buffer(conf_name), "", "", 0, 0, 1, trunk_ref->chan, NULL);
06083    if (conf) {
06084       conf_run(trunk_ref->chan, conf, &conf_flags, NULL);
06085       dispose_conf(conf);
06086       conf = NULL;
06087    }
06088    trunk_ref->chan = NULL;
06089    if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
06090       trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
06091       ast_str_append(&conf_name, 0, ",K");
06092       admin_exec(NULL, ast_str_buffer(conf_name));
06093       trunk_ref->trunk->hold_stations = 0;
06094       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06095    }
06096 
06097    ast_dial_join(station->dial);
06098    ast_dial_destroy(station->dial);
06099    station->dial = NULL;
06100    ast_free(conf_name);
06101 
06102    return NULL;
06103 }
06104 
06105 static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk);
06106 
06107 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
06108 {
06109    char buf[80];
06110    struct sla_station_ref *station_ref;
06111 
06112    snprintf(buf, sizeof(buf), "SLA_%s,K", ringing_trunk->trunk->name);
06113    admin_exec(NULL, buf);
06114    sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06115 
06116    while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry))) {
06117       ao2_ref(station_ref, -1);
06118    }
06119 
06120    sla_ringing_trunk_destroy(ringing_trunk);
06121 }
06122 
06123 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
06124    enum sla_station_hangup hangup)
06125 {
06126    struct sla_ringing_trunk *ringing_trunk;
06127    struct sla_trunk_ref *trunk_ref;
06128    struct sla_station_ref *station_ref;
06129 
06130    ast_dial_join(ringing_station->station->dial);
06131    ast_dial_destroy(ringing_station->station->dial);
06132    ringing_station->station->dial = NULL;
06133 
06134    if (hangup == SLA_STATION_HANGUP_NORMAL)
06135       goto done;
06136 
06137    /* If the station is being hung up because of a timeout, then add it to the
06138     * list of timed out stations on each of the ringing trunks.  This is so
06139     * that when doing further processing to figure out which stations should be
06140     * ringing, which trunk to answer, determining timeouts, etc., we know which
06141     * ringing trunks we should ignore. */
06142    AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
06143       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
06144          if (ringing_trunk->trunk == trunk_ref->trunk)
06145             break;
06146       }
06147       if (!trunk_ref)
06148          continue;
06149       if (!(station_ref = sla_create_station_ref(ringing_station->station)))
06150          continue;
06151       AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
06152    }
06153 
06154 done:
06155    sla_ringing_station_destroy(ringing_station);
06156 }
06157 
06158 static void sla_dial_state_callback(struct ast_dial *dial)
06159 {
06160    sla_queue_event(SLA_EVENT_DIAL_STATE);
06161 }
06162 
06163 /*! \brief Check to see if dialing this station already timed out for this ringing trunk
06164  * \note Assumes sla.lock is locked
06165  */
06166 static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
06167    const struct sla_station *station)
06168 {
06169    struct sla_station_ref *timed_out_station;
06170 
06171    AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
06172       if (station == timed_out_station->station)
06173          return 1;
06174    }
06175 
06176    return 0;
06177 }
06178 
06179 /*! \brief Choose the highest priority ringing trunk for a station
06180  * \param station the station
06181  * \param rm remove the ringing trunk once selected
06182  * \param trunk_ref a place to store the pointer to this stations reference to
06183  *        the selected trunk
06184  * \return a pointer to the selected ringing trunk, or NULL if none found
06185  * \note Assumes that sla.lock is locked
06186  */
06187 static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station, 
06188    struct sla_trunk_ref **trunk_ref, int rm)
06189 {
06190    struct sla_trunk_ref *s_trunk_ref;
06191    struct sla_ringing_trunk *ringing_trunk = NULL;
06192 
06193    AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
06194       AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06195          /* Make sure this is the trunk we're looking for */
06196          if (s_trunk_ref->trunk != ringing_trunk->trunk)
06197             continue;
06198 
06199          /* This trunk on the station is ringing.  But, make sure this station
06200           * didn't already time out while this trunk was ringing. */
06201          if (sla_check_timed_out_station(ringing_trunk, station))
06202             continue;
06203 
06204          if (rm)
06205             AST_LIST_REMOVE_CURRENT(entry);
06206 
06207          if (trunk_ref) {
06208             ao2_ref(s_trunk_ref, 1);
06209             *trunk_ref = s_trunk_ref;
06210          }
06211 
06212          break;
06213       }
06214       AST_LIST_TRAVERSE_SAFE_END;
06215    
06216       if (ringing_trunk)
06217          break;
06218    }
06219 
06220    return ringing_trunk;
06221 }
06222 
06223 static void sla_handle_dial_state_event(void)
06224 {
06225    struct sla_ringing_station *ringing_station;
06226 
06227    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
06228       RAII_VAR(struct sla_trunk_ref *, s_trunk_ref, NULL, ao2_cleanup);
06229       struct sla_ringing_trunk *ringing_trunk = NULL;
06230       struct run_station_args args;
06231       enum ast_dial_result dial_res;
06232       pthread_t dont_care;
06233       ast_mutex_t cond_lock;
06234       ast_cond_t cond;
06235 
06236       switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
06237       case AST_DIAL_RESULT_HANGUP:
06238       case AST_DIAL_RESULT_INVALID:
06239       case AST_DIAL_RESULT_FAILED:
06240       case AST_DIAL_RESULT_TIMEOUT:
06241       case AST_DIAL_RESULT_UNANSWERED:
06242          AST_LIST_REMOVE_CURRENT(entry);
06243          sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
06244          break;
06245       case AST_DIAL_RESULT_ANSWERED:
06246          AST_LIST_REMOVE_CURRENT(entry);
06247          /* Find the appropriate trunk to answer. */
06248          ast_mutex_lock(&sla.lock);
06249          ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
06250          ast_mutex_unlock(&sla.lock);
06251          if (!ringing_trunk) {
06252             /* This case happens in a bit of a race condition.  If two stations answer
06253              * the outbound call at the same time, the first one will get connected to
06254              * the trunk.  When the second one gets here, it will not see any trunks
06255              * ringing so we have no idea what to conect it to.  So, we just hang up
06256              * on it. */
06257             ast_debug(1, "Found no ringing trunk for station '%s' to answer!\n", ringing_station->station->name);
06258             ast_dial_join(ringing_station->station->dial);
06259             ast_dial_destroy(ringing_station->station->dial);
06260             ringing_station->station->dial = NULL;
06261             sla_ringing_station_destroy(ringing_station);
06262             break;
06263          }
06264          /* Track the channel that answered this trunk */
06265          s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
06266          /* Actually answer the trunk */
06267          answer_trunk_chan(ringing_trunk->trunk->chan);
06268          sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06269          /* Now, start a thread that will connect this station to the trunk.  The rest of
06270           * the code here sets up the thread and ensures that it is able to save the arguments
06271           * before they are no longer valid since they are allocated on the stack. */
06272          ao2_ref(s_trunk_ref, 1);
06273          args.trunk_ref = s_trunk_ref;
06274          ao2_ref(ringing_station->station, 1);
06275          args.station = ringing_station->station;
06276          args.cond = &cond;
06277          args.cond_lock = &cond_lock;
06278          sla_ringing_trunk_destroy(ringing_trunk);
06279          sla_ringing_station_destroy(ringing_station);
06280          ast_mutex_init(&cond_lock);
06281          ast_cond_init(&cond, NULL);
06282          ast_mutex_lock(&cond_lock);
06283          ast_pthread_create_detached_background(&dont_care, NULL, run_station, &args);
06284          ast_cond_wait(&cond, &cond_lock);
06285          ast_mutex_unlock(&cond_lock);
06286          ast_mutex_destroy(&cond_lock);
06287          ast_cond_destroy(&cond);
06288          break;
06289       case AST_DIAL_RESULT_TRYING:
06290       case AST_DIAL_RESULT_RINGING:
06291       case AST_DIAL_RESULT_PROGRESS:
06292       case AST_DIAL_RESULT_PROCEEDING:
06293          break;
06294       }
06295       if (dial_res == AST_DIAL_RESULT_ANSWERED) {
06296          /* Queue up reprocessing ringing trunks, and then ringing stations again */
06297          sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06298          sla_queue_event(SLA_EVENT_DIAL_STATE);
06299          break;
06300       }
06301    }
06302    AST_LIST_TRAVERSE_SAFE_END;
06303 }
06304 
06305 /*! \brief Check to see if this station is already ringing 
06306  * \note Assumes sla.lock is locked 
06307  */
06308 static int sla_check_ringing_station(const struct sla_station *station)
06309 {
06310    struct sla_ringing_station *ringing_station;
06311 
06312    AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
06313       if (station == ringing_station->station)
06314          return 1;
06315    }
06316 
06317    return 0;
06318 }
06319 <