Sat Apr 26 2014 22:01:26

Asterisk developer's documentation


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 /*** MODULEINFO
00033    <depend>dahdi</depend>
00034    <defaultenabled>yes</defaultenabled>
00035    <support_level>extended</support_level>
00036    <replacement>app_confbridge</replacement>
00037  ***/
00038 
00039 #include "asterisk.h"
00040 
00041 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 404773 $")
00042 
00043 #include <dahdi/user.h>
00044 
00045 #include "asterisk/lock.h"
00046 #include "asterisk/file.h"
00047 #include "asterisk/channel.h"
00048 #include "asterisk/pbx.h"
00049 #include "asterisk/module.h"
00050 #include "asterisk/config.h"
00051 #include "asterisk/app.h"
00052 #include "asterisk/dsp.h"
00053 #include "asterisk/musiconhold.h"
00054 #include "asterisk/manager.h"
00055 #include "asterisk/cli.h"
00056 #include "asterisk/say.h"
00057 #include "asterisk/utils.h"
00058 #include "asterisk/translate.h"
00059 #include "asterisk/ulaw.h"
00060 #include "asterisk/astobj2.h"
00061 #include "asterisk/devicestate.h"
00062 #include "asterisk/dial.h"
00063 #include "asterisk/causes.h"
00064 #include "asterisk/paths.h"
00065 #include "asterisk/data.h"
00066 #include "asterisk/test.h"
00067 
00068 #include "enter.h"
00069 #include "leave.h"
00070 
00071 /*** DOCUMENTATION
00072    <application name="MeetMe" language="en_US">
00073       <synopsis>
00074          MeetMe conference bridge.
00075       </synopsis>
00076       <syntax>
00077          <parameter name="confno">
00078             <para>The conference number</para>
00079          </parameter>
00080          <parameter name="options">
00081             <optionlist>
00082                <option name="a">
00083                   <para>Set admin mode.</para>
00084                </option>
00085                <option name="A">
00086                   <para>Set marked mode.</para>
00087                </option>
00088                <option name="b">
00089                   <para>Run AGI script specified in <variable>MEETME_AGI_BACKGROUND</variable>
00090                   Default: <literal>conf-background.agi</literal>.</para>
00091                   <note><para>This does not work with non-DAHDI channels in the same
00092                   conference).</para></note>
00093                </option>
00094                <option name="c">
00095                   <para>Announce user(s) count on joining a conference.</para>
00096                </option>
00097                <option name="C">
00098                   <para>Continue in dialplan when kicked out of conference.</para>
00099                </option>
00100                <option name="d">
00101                   <para>Dynamically add conference.</para>
00102                </option>
00103                <option name="D">
00104                   <para>Dynamically add conference, prompting for a PIN.</para>
00105                </option>
00106                <option name="e">
00107                   <para>Select an empty conference.</para>
00108                </option>
00109                <option name="E">
00110                   <para>Select an empty pinless conference.</para>
00111                </option>
00112                <option name="F">
00113                   <para>Pass DTMF through the conference.</para>
00114                </option>
00115                <option name="G">
00116                   <argument name="x" required="true">
00117                      <para>The file to playback</para>
00118                   </argument>
00119                   <para>Play an intro announcement in conference.</para>
00120                </option>
00121                <option name="i">
00122                   <para>Announce user join/leave with review.</para>
00123                </option>
00124                <option name="I">
00125                   <para>Announce user join/leave without review.</para>
00126                </option>
00127                <option name="k">
00128                   <para>Close the conference if there's only one active participant left at exit.</para>
00129                </option>
00130                <option name="l">
00131                   <para>Set listen only mode (Listen only, no talking).</para>
00132                </option>
00133                <option name="m">
00134                   <para>Set initially muted.</para>
00135                </option>
00136                <option name="M" hasparams="optional">
00137                   <para>Enable music on hold when the conference has a single caller. Optionally,
00138                   specify a musiconhold class to use. If one is not provided, it will use the
00139                   channel's currently set music class, or <literal>default</literal>.</para>
00140                   <argument name="class" required="true" />
00141                </option>
00142                <option name="n">
00143                   <para>Disable the denoiser. By default, if <literal>func_speex</literal> is loaded, Asterisk
00144                   will apply a denoiser to channels in the MeetMe conference. However, channel
00145                   drivers that present audio with a varying rate will experience degraded
00146                   performance with a denoiser attached. This parameter allows a channel joining
00147                   the conference to choose not to have a denoiser attached without having to
00148                   unload <literal>func_speex</literal>.</para>
00149                </option>
00150                <option name="o">
00151                   <para>Set talker optimization - treats talkers who aren't speaking as
00152                   being muted, meaning (a) No encode is done on transmission and (b)
00153                   Received audio that is not registered as talking is omitted causing no
00154                   buildup in background noise.</para>
00155                </option>
00156                <option name="p" hasparams="optional">
00157                   <para>Allow user to exit the conference by pressing <literal>#</literal> (default)
00158                   or any of the defined keys. Dial plan execution will continue at the next
00159                   priority following MeetMe. The key used is set to channel variable
00160                   <variable>MEETME_EXIT_KEY</variable>.</para>
00161                   <argument name="keys" required="true" />
00162                   <note>
00163                      <para>Option <literal>s</literal> has priority for <literal>*</literal>
00164                      since it cannot change its activation code.</para>
00165                   </note>
00166                </option>
00167                <option name="P">
00168                   <para>Always prompt for the pin even if it is specified.</para>
00169                </option>
00170                <option name="q">
00171                   <para>Quiet mode (don't play enter/leave sounds).</para>
00172                </option>
00173                <option name="r">
00174                   <para>Record conference (records as <variable>MEETME_RECORDINGFILE</variable>
00175                   using format <variable>MEETME_RECORDINGFORMAT</variable>. Default filename is
00176                   <literal>meetme-conf-rec-${CONFNO}-${UNIQUEID}</literal> and the default format is
00177                   wav.</para>
00178                </option>
00179                <option name="s">
00180                   <para>Present menu (user or admin) when <literal>*</literal> is received
00181                   (send to menu).</para>
00182                </option>
00183                <option name="t">
00184                   <para>Set talk only mode. (Talk only, no listening).</para>
00185                </option>
00186                <option name="T">
00187                   <para>Set talker detection (sent to manager interface and meetme list).</para>
00188                </option>
00189                <option name="v" hasparams="optional">
00190                   <para>Announce when a user is joining or leaving the conference.  Use the voicemail greeting as the announcement.
00191                    If the i or I options are set, the application will fall back to them if no voicemail greeting can be found.</para>
00192                   <argument name="mailbox@[context]" required="true">
00193                      <para>The mailbox and voicemail context to play from.  If no context provided, assumed context is default.</para>
00194                   </argument>
00195                </option>
00196                <option name="w" hasparams="optional">
00197                   <para>Wait until the marked user enters the conference.</para>
00198                   <argument name="secs" required="true" />
00199                </option>
00200                <option name="x">
00201                   <para>Leave the conference when the last marked user leaves.</para>
00202                </option>
00203                <option name="X">
00204                   <para>Allow user to exit the conference by entering a valid single digit
00205                   extension <variable>MEETME_EXIT_CONTEXT</variable> or the current context
00206                   if that variable is not defined.</para>
00207                   <note>
00208                      <para>Option <literal>s</literal> has priority for <literal>*</literal>
00209                      since it cannot change its activation code.</para>
00210                   </note>
00211                </option>
00212                <option name="1">
00213                   <para>Do not play message when first person enters</para>
00214                </option>
00215                <option name="S">
00216                   <para>Kick the user <replaceable>x</replaceable> seconds <emphasis>after</emphasis> he entered into
00217                   the conference.</para>
00218                   <argument name="x" required="true" />
00219                </option>
00220                <option name="L" argsep=":">
00221                   <para>Limit the conference to <replaceable>x</replaceable> ms. Play a warning when
00222                   <replaceable>y</replaceable> ms are left. Repeat the warning every <replaceable>z</replaceable> ms.
00223                   The following special variables can be used with this option:</para>
00224                   <variablelist>
00225                      <variable name="CONF_LIMIT_TIMEOUT_FILE">
00226                         <para>File to play when time is up.</para>
00227                      </variable>
00228                      <variable name="CONF_LIMIT_WARNING_FILE">
00229                         <para>File to play as warning if <replaceable>y</replaceable> is defined. The
00230                         default is to say the time remaining.</para>
00231                      </variable>
00232                   </variablelist>
00233                   <argument name="x" />
00234                   <argument name="y" />
00235                   <argument name="z" />
00236                </option>
00237             </optionlist>
00238          </parameter>
00239          <parameter name="pin" />
00240       </syntax>
00241       <description>
00242          <para>Enters the user into a specified MeetMe conference.  If the <replaceable>confno</replaceable>
00243          is omitted, the user will be prompted to enter one.  User can exit the conference by hangup, or
00244          if the <literal>p</literal> option is specified, by pressing <literal>#</literal>.</para>
00245          <note><para>The DAHDI kernel modules and a functional DAHDI timing source (see dahdi_test)
00246          must be present for conferencing to operate properly. In addition, the chan_dahdi channel driver
00247          must be loaded for the <literal>i</literal> and <literal>r</literal> options to operate at
00248          all.</para></note>
00249       </description>
00250       <see-also>
00251          <ref type="application">MeetMeCount</ref>
00252          <ref type="application">MeetMeAdmin</ref>
00253          <ref type="application">MeetMeChannelAdmin</ref>
00254       </see-also>
00255    </application>
00256    <application name="MeetMeCount" language="en_US">
00257       <synopsis>
00258          MeetMe participant count.
00259       </synopsis>
00260       <syntax>
00261          <parameter name="confno" required="true">
00262             <para>Conference number.</para>
00263          </parameter>
00264          <parameter name="var" />
00265       </syntax>
00266       <description>
00267          <para>Plays back the number of users in the specified MeetMe conference.
00268          If <replaceable>var</replaceable> is specified, playback will be skipped and the value
00269          will be returned in the variable. Upon application completion, MeetMeCount will hangup
00270          the channel, unless priority <literal>n+1</literal> exists, in which case priority progress will
00271          continue.</para>
00272       </description>
00273       <see-also>
00274          <ref type="application">MeetMe</ref>
00275       </see-also>
00276    </application>
00277    <application name="MeetMeAdmin" language="en_US">
00278       <synopsis>
00279          MeetMe conference administration.
00280       </synopsis>
00281       <syntax>
00282          <parameter name="confno" required="true" />
00283          <parameter name="command" required="true">
00284             <optionlist>
00285                <option name="e">
00286                   <para>Eject last user that joined.</para>
00287                </option>
00288                <option name="E">
00289                   <para>Extend conference end time, if scheduled.</para>
00290                </option>
00291                <option name="k">
00292                   <para>Kick one user out of conference.</para>
00293                </option>
00294                <option name="K">
00295                   <para>Kick all users out of conference.</para>
00296                </option>
00297                <option name="l">
00298                   <para>Unlock conference.</para>
00299                </option>
00300                <option name="L">
00301                   <para>Lock conference.</para>
00302                </option>
00303                <option name="m">
00304                   <para>Unmute one user.</para>
00305                </option>
00306                <option name="M">
00307                   <para>Mute one user.</para>
00308                </option>
00309                <option name="n">
00310                   <para>Unmute all users in the conference.</para>
00311                </option>
00312                <option name="N">
00313                   <para>Mute all non-admin users in the conference.</para>
00314                </option>
00315                <option name="r">
00316                   <para>Reset one user's volume settings.</para>
00317                </option>
00318                <option name="R">
00319                   <para>Reset all users volume settings.</para>
00320                </option>
00321                <option name="s">
00322                   <para>Lower entire conference speaking volume.</para>
00323                </option>
00324                <option name="S">
00325                   <para>Raise entire conference speaking volume.</para>
00326                </option>
00327                <option name="t">
00328                   <para>Lower one user's talk volume.</para>
00329                </option>
00330                <option name="T">
00331                   <para>Raise one user's talk volume.</para>
00332                </option>
00333                <option name="u">
00334                   <para>Lower one user's listen volume.</para>
00335                </option>
00336                <option name="U">
00337                   <para>Raise one user's listen volume.</para>
00338                </option>
00339                <option name="v">
00340                   <para>Lower entire conference listening volume.</para>
00341                </option>
00342                <option name="V">
00343                   <para>Raise entire conference listening volume.</para>
00344                </option>
00345             </optionlist>
00346          </parameter>
00347          <parameter name="user" />
00348       </syntax>
00349       <description>
00350          <para>Run admin <replaceable>command</replaceable> for conference <replaceable>confno</replaceable>.</para>
00351          <para>Will additionally set the variable <variable>MEETMEADMINSTATUS</variable> with one of
00352          the following values:</para>
00353          <variablelist>
00354             <variable name="MEETMEADMINSTATUS">
00355                <value name="NOPARSE">
00356                   Invalid arguments.
00357                </value>
00358                <value name="NOTFOUND">
00359                   User specified was not found.
00360                </value>
00361                <value name="FAILED">
00362                   Another failure occurred.
00363                </value>
00364                <value name="OK">
00365                   The operation was completed successfully.
00366                </value>
00367             </variable>
00368          </variablelist>
00369       </description>
00370       <see-also>
00371          <ref type="application">MeetMe</ref>
00372       </see-also>
00373    </application>
00374    <application name="MeetMeChannelAdmin" language="en_US">
00375       <synopsis>
00376          MeetMe conference Administration (channel specific).
00377       </synopsis>
00378       <syntax>
00379          <parameter name="channel" required="true" />
00380          <parameter name="command" required="true">
00381             <optionlist>
00382                <option name="k">
00383                   <para>Kick the specified user out of the conference he is in.</para>
00384                </option>
00385                <option name="m">
00386                   <para>Unmute the specified user.</para>
00387                </option>
00388                <option name="M">
00389                   <para>Mute the specified user.</para>
00390                </option>
00391             </optionlist>
00392          </parameter>
00393       </syntax>
00394       <description>
00395          <para>Run admin <replaceable>command</replaceable> for a specific
00396          <replaceable>channel</replaceable> in any conference.</para>
00397       </description>
00398    </application>
00399    <application name="SLAStation" language="en_US">
00400       <synopsis>
00401          Shared Line Appearance Station.
00402       </synopsis>
00403       <syntax>
00404          <parameter name="station" required="true">
00405             <para>Station name</para>
00406          </parameter>
00407       </syntax>
00408       <description>
00409          <para>This application should be executed by an SLA station. The argument depends
00410          on how the call was initiated. If the phone was just taken off hook, then the argument
00411          <replaceable>station</replaceable> should be just the station name. If the call was
00412          initiated by pressing a line key, then the station name should be preceded by an underscore
00413          and the trunk name associated with that line button.</para>
00414          <para>For example: <literal>station1_line1</literal></para>
00415          <para>On exit, this application will set the variable <variable>SLASTATION_STATUS</variable> to
00416          one of the following values:</para>
00417          <variablelist>
00418             <variable name="SLASTATION_STATUS">
00419                <value name="FAILURE" />
00420                <value name="CONGESTION" />
00421                <value name="SUCCESS" />
00422             </variable>
00423          </variablelist>
00424       </description>
00425    </application>
00426    <application name="SLATrunk" language="en_US">
00427       <synopsis>
00428          Shared Line Appearance Trunk.
00429       </synopsis>
00430       <syntax>
00431          <parameter name="trunk" required="true">
00432             <para>Trunk name</para>
00433          </parameter>
00434          <parameter name="options">
00435             <optionlist>
00436                <option name="M" hasparams="optional">
00437                   <para>Play back the specified MOH <replaceable>class</replaceable>
00438                   instead of ringing</para>
00439                   <argument name="class" required="true" />
00440                </option>
00441             </optionlist>
00442          </parameter>
00443       </syntax>
00444       <description>
00445          <para>This application should be executed by an SLA trunk on an inbound call. The channel calling
00446          this application should correspond to the SLA trunk with the name <replaceable>trunk</replaceable>
00447          that is being passed as an argument.</para>
00448          <para>On exit, this application will set the variable <variable>SLATRUNK_STATUS</variable> to
00449          one of the following values:</para>
00450          <variablelist>
00451             <variable name="SLATRUNK_STATUS">
00452                <value name="FAILURE" />
00453                <value name="SUCCESS" />
00454                <value name="UNANSWERED" />
00455                <value name="RINGTIMEOUT" />
00456             </variable>
00457          </variablelist>
00458       </description>
00459    </application>
00460    <function name="MEETME_INFO" language="en_US">
00461       <synopsis>
00462          Query a given conference of various properties.
00463       </synopsis>
00464       <syntax>
00465          <parameter name="keyword" required="true">
00466             <para>Options:</para>
00467             <enumlist>
00468                <enum name="lock">
00469                   <para>Boolean of whether the corresponding conference is locked.</para>
00470                </enum>
00471                <enum name="parties">
00472                   <para>Number of parties in a given conference</para>
00473                </enum>
00474                <enum name="activity">
00475                   <para>Duration of conference in seconds.</para>
00476                </enum>
00477                <enum name="dynamic">
00478                   <para>Boolean of whether the corresponding conference is dynamic.</para>
00479                </enum>
00480             </enumlist>
00481          </parameter>
00482          <parameter name="confno" required="true">
00483             <para>Conference number to retrieve information from.</para>
00484          </parameter>
00485       </syntax>
00486       <description />
00487       <see-also>
00488          <ref type="application">MeetMe</ref>
00489          <ref type="application">MeetMeCount</ref>
00490          <ref type="application">MeetMeAdmin</ref>
00491          <ref type="application">MeetMeChannelAdmin</ref>
00492       </see-also>
00493    </function>
00494    <manager name="MeetmeMute" language="en_US">
00495       <synopsis>
00496          Mute a Meetme user.
00497       </synopsis>
00498       <syntax>
00499          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00500          <parameter name="Meetme" required="true" />
00501          <parameter name="Usernum" required="true" />
00502       </syntax>
00503       <description>
00504       </description>
00505    </manager>
00506    <manager name="MeetmeUnmute" language="en_US">
00507       <synopsis>
00508          Unmute a Meetme user.
00509       </synopsis>
00510       <syntax>
00511          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00512          <parameter name="Meetme" required="true" />
00513          <parameter name="Usernum" required="true" />
00514       </syntax>
00515       <description>
00516       </description>
00517    </manager>
00518    <manager name="MeetmeList" language="en_US">
00519       <synopsis>
00520          List participants in a conference.
00521       </synopsis>
00522       <syntax>
00523          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00524          <parameter name="Conference" required="false">
00525             <para>Conference number.</para>
00526          </parameter>
00527       </syntax>
00528       <description>
00529          <para>Lists all users in a particular MeetMe conference.
00530          MeetmeList will follow as separate events, followed by a final event called
00531          MeetmeListComplete.</para>
00532       </description>
00533    </manager>
00534    <manager name="MeetmeListRooms" language="en_US">
00535       <synopsis>
00536          List active conferences.
00537       </synopsis>
00538       <syntax>
00539          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00540       </syntax>
00541       <description>
00542          <para>Lists data about all active conferences.
00543             MeetmeListRooms will follow as separate events, followed by a final event called
00544             MeetmeListRoomsComplete.</para>
00545       </description>
00546    </manager>
00547  ***/
00548 
00549 #define CONFIG_FILE_NAME   "meetme.conf"
00550 #define SLA_CONFIG_FILE    "sla.conf"
00551 #define STR_CONCISE        "concise"
00552 
00553 /*! each buffer is 20ms, so this is 640ms total */
00554 #define DEFAULT_AUDIO_BUFFERS  32
00555 
00556 /*! String format for scheduled conferences */
00557 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
00558 
00559 enum {
00560    ADMINFLAG_MUTED =     (1 << 1), /*!< User is muted */
00561    ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
00562    ADMINFLAG_KICKME =    (1 << 3),  /*!< User has been kicked */
00563    /*! User has requested to speak */
00564    ADMINFLAG_T_REQUEST = (1 << 4),
00565    ADMINFLAG_HANGUP = (1 << 5),  /*!< User will be leaving the conference */
00566 };
00567 
00568 #define MEETME_DELAYDETECTTALK     300
00569 #define MEETME_DELAYDETECTENDTALK  1000
00570 
00571 #define AST_FRAME_BITS  32
00572 
00573 enum volume_action {
00574    VOL_UP,
00575    VOL_DOWN
00576 };
00577 
00578 enum entrance_sound {
00579    ENTER,
00580    LEAVE
00581 };
00582 
00583 enum recording_state {
00584    MEETME_RECORD_OFF,
00585    MEETME_RECORD_STARTED,
00586    MEETME_RECORD_ACTIVE,
00587    MEETME_RECORD_TERMINATE
00588 };
00589 
00590 #define CONF_SIZE  320
00591 
00592 enum {
00593    /*! user has admin access on the conference */
00594    CONFFLAG_ADMIN = (1 << 0),
00595    /*! If set the user can only receive audio from the conference */
00596    CONFFLAG_MONITOR = (1 << 1),
00597    /*! If set asterisk will exit conference when key defined in p() option is pressed */
00598    CONFFLAG_KEYEXIT = (1 << 2),
00599    /*! If set asterisk will provide a menu to the user when '*' is pressed */
00600    CONFFLAG_STARMENU = (1 << 3),
00601    /*! If set the use can only send audio to the conference */
00602    CONFFLAG_TALKER = (1 << 4),
00603    /*! If set there will be no enter or leave sounds */
00604    CONFFLAG_QUIET = (1 << 5),
00605    /*! If set, when user joins the conference, they will be told the number 
00606     *  of users that are already in */
00607    CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
00608    /*! Set to run AGI Script in Background */
00609    CONFFLAG_AGI = (1 << 7),
00610    /*! Set to have music on hold when user is alone in conference */
00611    CONFFLAG_MOH = (1 << 8),
00612    /*! If set, the channel will leave the conference if all marked users leave */
00613    CONFFLAG_MARKEDEXIT = (1 << 9),
00614    /*! If set, the MeetMe will wait until a marked user enters */
00615    CONFFLAG_WAITMARKED = (1 << 10),
00616    /*! If set, the MeetMe will exit to the specified context */
00617    CONFFLAG_EXIT_CONTEXT = (1 << 11),
00618    /*! If set, the user will be marked */
00619    CONFFLAG_MARKEDUSER = (1 << 12),
00620    /*! If set, user will be ask record name on entry of conference */
00621    CONFFLAG_INTROUSER = (1 << 13),
00622    /*! If set, the MeetMe will be recorded */
00623    CONFFLAG_RECORDCONF = (1<< 14),
00624    /*! If set, the user will be monitored if the user is talking or not */
00625    CONFFLAG_MONITORTALKER = (1 << 15),
00626    CONFFLAG_DYNAMIC = (1 << 16),
00627    CONFFLAG_DYNAMICPIN = (1 << 17),
00628    CONFFLAG_EMPTY = (1 << 18),
00629    CONFFLAG_EMPTYNOPIN = (1 << 19),
00630    CONFFLAG_ALWAYSPROMPT = (1 << 20),
00631    /*! If set, treat talking users as muted users */
00632    CONFFLAG_OPTIMIZETALKER = (1 << 21),
00633    /*! If set, won't speak the extra prompt when the first person 
00634     *  enters the conference */
00635    CONFFLAG_NOONLYPERSON = (1 << 22),
00636    /*! If set, user will be asked to record name on entry of conference 
00637     *  without review */
00638    CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
00639    /*! If set, the user will be initially self-muted */
00640    CONFFLAG_STARTMUTED = (1 << 24),
00641    /*! Pass DTMF through the conference */
00642    CONFFLAG_PASS_DTMF = (1 << 25),
00643    CONFFLAG_SLA_STATION = (1 << 26),
00644    CONFFLAG_SLA_TRUNK = (1 << 27),
00645    /*! If set, the user should continue in the dialplan if kicked out */
00646    CONFFLAG_KICK_CONTINUE = (1 << 28),
00647    CONFFLAG_DURATION_STOP = (1 << 29),
00648    CONFFLAG_DURATION_LIMIT = (1 << 30),
00649 };
00650 
00651 /* These flags are defined separately because we ran out of bits that an enum can be used to represent. 
00652    If you add new flags, be sure to do it in the same way that these are. */
00653 /*! Do not write any audio to this channel until the state is up. */
00654 #define CONFFLAG_NO_AUDIO_UNTIL_UP  (1ULL << 31)
00655 #define CONFFLAG_INTROMSG           (1ULL << 32) /*!< If set play an intro announcement at start of conference */
00656 #define CONFFLAG_INTROUSER_VMREC    (1ULL << 33)
00657 /*! If there's only one person left in a conference when someone leaves, kill the conference */
00658 #define CONFFLAG_KILL_LAST_MAN_STANDING (1ULL << 34)
00659 /*! If set, don't enable a denoiser for the channel */
00660 #define CONFFLAG_DONT_DENOISE       (1ULL << 35)
00661 
00662 enum {
00663    OPT_ARG_WAITMARKED = 0,
00664    OPT_ARG_EXITKEYS   = 1,
00665    OPT_ARG_DURATION_STOP = 2,
00666    OPT_ARG_DURATION_LIMIT = 3,
00667    OPT_ARG_MOH_CLASS = 4,
00668    OPT_ARG_INTROMSG = 5,
00669    OPT_ARG_INTROUSER_VMREC = 6,
00670    OPT_ARG_ARRAY_SIZE = 7,
00671 };
00672 
00673 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
00674    AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
00675    AST_APP_OPTION('a', CONFFLAG_ADMIN ),
00676    AST_APP_OPTION('b', CONFFLAG_AGI ),
00677    AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
00678    AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
00679    AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
00680    AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
00681    AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
00682    AST_APP_OPTION('e', CONFFLAG_EMPTY ),
00683    AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
00684    AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
00685    AST_APP_OPTION_ARG('v', CONFFLAG_INTROUSER_VMREC , OPT_ARG_INTROUSER_VMREC),
00686    AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
00687    AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
00688    AST_APP_OPTION('k', CONFFLAG_KILL_LAST_MAN_STANDING ),
00689    AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
00690    AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
00691    AST_APP_OPTION('n', CONFFLAG_DONT_DENOISE ),
00692    AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
00693    AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
00694    AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
00695    AST_APP_OPTION('q', CONFFLAG_QUIET ),
00696    AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
00697    AST_APP_OPTION('s', CONFFLAG_STARMENU ),
00698    AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
00699    AST_APP_OPTION('l', CONFFLAG_MONITOR ),
00700    AST_APP_OPTION('t', CONFFLAG_TALKER ),
00701    AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
00702    AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
00703    AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
00704    AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
00705    AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
00706    AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
00707 END_OPTIONS );
00708 
00709 static const char * const app = "MeetMe";
00710 static const char * const app2 = "MeetMeCount";
00711 static const char * const app3 = "MeetMeAdmin";
00712 static const char * const app4 = "MeetMeChannelAdmin";
00713 static const char * const slastation_app = "SLAStation";
00714 static const char * const slatrunk_app = "SLATrunk";
00715 
00716 /* Lookup RealTime conferences based on confno and current time */
00717 static int rt_schedule;
00718 static int fuzzystart;
00719 static int earlyalert;
00720 static int endalert;
00721 static int extendby;
00722 
00723 /*! Log participant count to the RealTime backend */
00724 static int rt_log_members;
00725 
00726 #define MAX_CONFNUM 80
00727 #define MAX_PIN     80
00728 #define OPTIONS_LEN 100
00729 
00730 /* Enough space for "<conference #>,<pin>,<admin pin>" followed by a 0 byte. */
00731 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
00732 
00733 enum announcetypes {
00734    CONF_HASJOIN,
00735    CONF_HASLEFT
00736 };
00737 
00738 struct announce_listitem {
00739    AST_LIST_ENTRY(announce_listitem) entry;
00740    char namerecloc[PATH_MAX];          /*!< Name Recorded file Location */
00741    char language[MAX_LANGUAGE];
00742    struct ast_channel *confchan;
00743    int confusers;
00744    int vmrec;
00745    enum announcetypes announcetype;
00746 };
00747 
00748 /*! \brief The MeetMe Conference object */
00749 struct ast_conference {
00750    ast_mutex_t playlock;                   /*!< Conference specific lock (players) */
00751    ast_mutex_t listenlock;                 /*!< Conference specific lock (listeners) */
00752    char confno[MAX_CONFNUM];               /*!< Conference */
00753    struct ast_channel *chan;               /*!< Announcements channel */
00754    struct ast_channel *lchan;              /*!< Listen/Record channel */
00755    int fd;                                 /*!< Announcements fd */
00756    int dahdiconf;                            /*!< DAHDI Conf # */
00757    int users;                              /*!< Number of active users */
00758    int markedusers;                        /*!< Number of marked users */
00759    int maxusers;                           /*!< Participant limit if scheduled */
00760    int endalert;                           /*!< When to play conf ending message */
00761    time_t start;                           /*!< Start time (s) */
00762    int refcount;                           /*!< reference count of usage */
00763    enum recording_state recording:2;       /*!< recording status */
00764    unsigned int isdynamic:1;               /*!< Created on the fly? */
00765    unsigned int locked:1;                  /*!< Is the conference locked? */
00766    unsigned int gmuted:1;                  /*!< Is the conference globally muted? (all non-admins) */
00767    pthread_t recordthread;                 /*!< thread for recording */
00768    ast_mutex_t recordthreadlock;           /*!< control threads trying to start recordthread */
00769    pthread_attr_t attr;                    /*!< thread attribute */
00770    char *recordingfilename;                /*!< Filename to record the Conference into */
00771    char *recordingformat;                  /*!< Format to record the Conference in */
00772    char pin[MAX_PIN];                      /*!< If protected by a PIN */
00773    char pinadmin[MAX_PIN];                 /*!< If protected by a admin PIN */
00774    char uniqueid[32];
00775    long endtime;                           /*!< When to end the conf if scheduled */
00776    const char *useropts;                   /*!< RealTime user flags */
00777    const char *adminopts;                  /*!< RealTime moderator flags */
00778    const char *bookid;                     /*!< RealTime conference id */
00779    struct ast_frame *transframe[32];
00780    struct ast_frame *origframe;
00781    struct ast_trans_pvt *transpath[32];
00782    struct ao2_container *usercontainer;
00783    AST_LIST_ENTRY(ast_conference) list;
00784    /* announce_thread related data */
00785    pthread_t announcethread;
00786    ast_mutex_t announcethreadlock;
00787    unsigned int announcethread_stop:1;
00788    ast_cond_t announcelist_addition;
00789    AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
00790    ast_mutex_t announcelistlock;
00791 };
00792 
00793 static AST_LIST_HEAD_STATIC(confs, ast_conference);
00794 
00795 static unsigned int conf_map[1024] = {0, };
00796 
00797 struct volume {
00798    int desired;                            /*!< Desired volume adjustment */
00799    int actual;                             /*!< Actual volume adjustment (for channels that can't adjust) */
00800 };
00801 
00802 /*! \brief The MeetMe User object */
00803 struct ast_conf_user {
00804    int user_no;                            /*!< User Number */
00805    struct ast_flags64 userflags;           /*!< Flags as set in the conference */
00806    int adminflags;                         /*!< Flags set by the Admin */
00807    struct ast_channel *chan;               /*!< Connected channel */
00808    int talking;                            /*!< Is user talking */
00809    int dahdichannel;                       /*!< Is a DAHDI channel */
00810    char usrvalue[50];                      /*!< Custom User Value */
00811    char namerecloc[PATH_MAX];    /*!< Name Recorded file Location */
00812    time_t jointime;                        /*!< Time the user joined the conference */
00813    time_t kicktime;                        /*!< Time the user will be kicked from the conference */
00814    struct timeval start_time;              /*!< Time the user entered into the conference */
00815    long timelimit;                         /*!< Time limit for the user to be in the conference L(x:y:z) */
00816    long play_warning;                      /*!< Play a warning when 'y' ms are left */
00817    long warning_freq;                      /*!< Repeat the warning every 'z' ms */
00818    const char *warning_sound;              /*!< File to play as warning if 'y' is defined */
00819    const char *end_sound;                  /*!< File to play when time is up. */
00820    struct volume talk;
00821    struct volume listen;
00822    AST_LIST_ENTRY(ast_conf_user) list;
00823 };
00824 
00825 enum sla_which_trunk_refs {
00826    ALL_TRUNK_REFS,
00827    INACTIVE_TRUNK_REFS,
00828 };
00829 
00830 enum sla_trunk_state {
00831    SLA_TRUNK_STATE_IDLE,
00832    SLA_TRUNK_STATE_RINGING,
00833    SLA_TRUNK_STATE_UP,
00834    SLA_TRUNK_STATE_ONHOLD,
00835    SLA_TRUNK_STATE_ONHOLD_BYME,
00836 };
00837 
00838 enum sla_hold_access {
00839    /*! This means that any station can put it on hold, and any station
00840     * can retrieve the call from hold. */
00841    SLA_HOLD_OPEN,
00842    /*! This means that only the station that put the call on hold may
00843     * retrieve it from hold. */
00844    SLA_HOLD_PRIVATE,
00845 };
00846 
00847 struct sla_trunk_ref;
00848 
00849 struct sla_station {
00850    AST_RWLIST_ENTRY(sla_station) entry;
00851    AST_DECLARE_STRING_FIELDS(
00852       AST_STRING_FIELD(name); 
00853       AST_STRING_FIELD(device);  
00854       AST_STRING_FIELD(autocontext);   
00855    );
00856    AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
00857    struct ast_dial *dial;
00858    /*! Ring timeout for this station, for any trunk.  If a ring timeout
00859     *  is set for a specific trunk on this station, that will take
00860     *  priority over this value. */
00861    unsigned int ring_timeout;
00862    /*! Ring delay for this station, for any trunk.  If a ring delay
00863     *  is set for a specific trunk on this station, that will take
00864     *  priority over this value. */
00865    unsigned int ring_delay;
00866    /*! This option uses the values in the sla_hold_access enum and sets the
00867     * access control type for hold on this station. */
00868    unsigned int hold_access:1;
00869    /*! Mark used during reload processing */
00870    unsigned int mark:1;
00871 };
00872 
00873 /*!
00874  * \brief A reference to a station
00875  *
00876  * This struct looks near useless at first glance.  However, its existence
00877  * in the list of stations in sla_trunk means that this station references
00878  * that trunk.  We use the mark to keep track of whether it needs to be
00879  * removed from the sla_trunk's list of stations during a reload.
00880  */
00881 struct sla_station_ref {
00882    AST_LIST_ENTRY(sla_station_ref) entry;
00883    struct sla_station *station;
00884    /*! Mark used during reload processing */
00885    unsigned int mark:1;
00886 };
00887 
00888 struct sla_trunk {
00889    AST_DECLARE_STRING_FIELDS(
00890       AST_STRING_FIELD(name);
00891       AST_STRING_FIELD(device);
00892       AST_STRING_FIELD(autocontext);   
00893    );
00894    AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
00895    /*! Number of stations that use this trunk */
00896    unsigned int num_stations;
00897    /*! Number of stations currently on a call with this trunk */
00898    unsigned int active_stations;
00899    /*! Number of stations that have this trunk on hold. */
00900    unsigned int hold_stations;
00901    struct ast_channel *chan;
00902    unsigned int ring_timeout;
00903    /*! If set to 1, no station will be able to join an active call with
00904     *  this trunk. */
00905    unsigned int barge_disabled:1;
00906    /*! This option uses the values in the sla_hold_access enum and sets the
00907     * access control type for hold on this trunk. */
00908    unsigned int hold_access:1;
00909    /*! Whether this trunk is currently on hold, meaning that once a station
00910     *  connects to it, the trunk channel needs to have UNHOLD indicated to it. */
00911    unsigned int on_hold:1;
00912    /*! Mark used during reload processing */
00913    unsigned int mark:1;
00914 };
00915 
00916 /*!
00917  * \brief A station's reference to a trunk
00918  *
00919  * An sla_station keeps a list of trunk_refs.  This holds metadata about the
00920  * stations usage of the trunk.
00921  */
00922 struct sla_trunk_ref {
00923    AST_LIST_ENTRY(sla_trunk_ref) entry;
00924    struct sla_trunk *trunk;
00925    enum sla_trunk_state state;
00926    struct ast_channel *chan;
00927    /*! Ring timeout to use when this trunk is ringing on this specific
00928     *  station.  This takes higher priority than a ring timeout set at
00929     *  the station level. */
00930    unsigned int ring_timeout;
00931    /*! Ring delay to use when this trunk is ringing on this specific
00932     *  station.  This takes higher priority than a ring delay set at
00933     *  the station level. */
00934    unsigned int ring_delay;
00935    /*! Mark used during reload processing */
00936    unsigned int mark:1;
00937 };
00938 
00939 static struct ao2_container *sla_stations;
00940 static struct ao2_container *sla_trunks;
00941 
00942 static const char sla_registrar[] = "SLA";
00943 
00944 /*! \brief Event types that can be queued up for the SLA thread */
00945 enum sla_event_type {
00946    /*! A station has put the call on hold */
00947    SLA_EVENT_HOLD,
00948    /*! The state of a dial has changed */
00949    SLA_EVENT_DIAL_STATE,
00950    /*! The state of a ringing trunk has changed */
00951    SLA_EVENT_RINGING_TRUNK,
00952 };
00953 
00954 struct sla_event {
00955    enum sla_event_type type;
00956    struct sla_station *station;
00957    struct sla_trunk_ref *trunk_ref;
00958    AST_LIST_ENTRY(sla_event) entry;
00959 };
00960 
00961 /*! \brief A station that failed to be dialed 
00962  * \note Only used by the SLA thread. */
00963 struct sla_failed_station {
00964    struct sla_station *station;
00965    struct timeval last_try;
00966    AST_LIST_ENTRY(sla_failed_station) entry;
00967 };
00968 
00969 /*! \brief A trunk that is ringing */
00970 struct sla_ringing_trunk {
00971    struct sla_trunk *trunk;
00972    /*! The time that this trunk started ringing */
00973    struct timeval ring_begin;
00974    AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
00975    AST_LIST_ENTRY(sla_ringing_trunk) entry;
00976 };
00977 
00978 enum sla_station_hangup {
00979    SLA_STATION_HANGUP_NORMAL,
00980    SLA_STATION_HANGUP_TIMEOUT,
00981 };
00982 
00983 /*! \brief A station that is ringing */
00984 struct sla_ringing_station {
00985    struct sla_station *station;
00986    /*! The time that this station started ringing */
00987    struct timeval ring_begin;
00988    AST_LIST_ENTRY(sla_ringing_station) entry;
00989 };
00990 
00991 /*!
00992  * \brief A structure for data used by the sla thread
00993  */
00994 static struct {
00995    /*! The SLA thread ID */
00996    pthread_t thread;
00997    ast_cond_t cond;
00998    ast_mutex_t lock;
00999    AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
01000    AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
01001    AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
01002    AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
01003    unsigned int stop:1;
01004    /*! Attempt to handle CallerID, even though it is known not to work
01005     *  properly in some situations. */
01006    unsigned int attempt_callerid:1;
01007 } sla = {
01008    .thread = AST_PTHREADT_NULL,
01009 };
01010 
01011 /*! \brief The number of audio buffers to be allocated on pseudo channels
01012  *  when in a conference */
01013 static int audio_buffers;
01014 
01015 /*! \brief Map 'volume' levels from -5 through +5 into decibel (dB) 
01016  *    settings for channel drivers.
01017  *
01018  *  \note these are not a straight linear-to-dB
01019  *  conversion... the numbers have been modified
01020  *  to give the user a better level of adjustability.
01021  */
01022 static const char gain_map[] = {
01023    -15,
01024    -13,
01025    -10,
01026    -6,
01027    0,
01028    0,
01029    0,
01030    6,
01031    10,
01032    13,
01033    15,
01034 };
01035 
01036 
01037 static int admin_exec(struct ast_channel *chan, const char *data);
01038 static void *recordthread(void *args);
01039 
01040 static const char *istalking(int x)
01041 {
01042    if (x > 0)
01043       return "(talking)";
01044    else if (x < 0)
01045       return "(unmonitored)";
01046    else 
01047       return "(not talking)";
01048 }
01049 
01050 static int careful_write(int fd, unsigned char *data, int len, int block)
01051 {
01052    int res;
01053    int x;
01054 
01055    while (len) {
01056       if (block) {
01057          x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
01058          res = ioctl(fd, DAHDI_IOMUX, &x);
01059       } else
01060          res = 0;
01061       if (res >= 0)
01062          res = write(fd, data, len);
01063       if (res < 1) {
01064          if (errno != EAGAIN) {
01065             ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
01066             return -1;
01067          } else
01068             return 0;
01069       }
01070       len -= res;
01071       data += res;
01072    }
01073 
01074    return 0;
01075 }
01076 
01077 static int set_talk_volume(struct ast_conf_user *user, int volume)
01078 {
01079    char gain_adjust;
01080 
01081    /* attempt to make the adjustment in the channel driver;
01082       if successful, don't adjust in the frame reading routine
01083    */
01084    gain_adjust = gain_map[volume + 5];
01085 
01086    return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
01087 }
01088 
01089 static int set_listen_volume(struct ast_conf_user *user, int volume)
01090 {
01091    char gain_adjust;
01092 
01093    /* attempt to make the adjustment in the channel driver;
01094       if successful, don't adjust in the frame reading routine
01095    */
01096    gain_adjust = gain_map[volume + 5];
01097 
01098    return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
01099 }
01100 
01101 static void tweak_volume(struct volume *vol, enum volume_action action)
01102 {
01103    switch (action) {
01104    case VOL_UP:
01105       switch (vol->desired) { 
01106       case 5:
01107          break;
01108       case 0:
01109          vol->desired = 2;
01110          break;
01111       case -2:
01112          vol->desired = 0;
01113          break;
01114       default:
01115          vol->desired++;
01116          break;
01117       }
01118       break;
01119    case VOL_DOWN:
01120       switch (vol->desired) {
01121       case -5:
01122          break;
01123       case 2:
01124          vol->desired = 0;
01125          break;
01126       case 0:
01127          vol->desired = -2;
01128          break;
01129       default:
01130          vol->desired--;
01131          break;
01132       }
01133    }
01134 }
01135 
01136 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
01137 {
01138    tweak_volume(&user->talk, action);
01139    /* attempt to make the adjustment in the channel driver;
01140       if successful, don't adjust in the frame reading routine
01141    */
01142    if (!set_talk_volume(user, user->talk.desired))
01143       user->talk.actual = 0;
01144    else
01145       user->talk.actual = user->talk.desired;
01146 }
01147 
01148 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
01149 {
01150    tweak_volume(&user->listen, action);
01151    /* attempt to make the adjustment in the channel driver;
01152       if successful, don't adjust in the frame reading routine
01153    */
01154    if (!set_listen_volume(user, user->listen.desired))
01155       user->listen.actual = 0;
01156    else
01157       user->listen.actual = user->listen.desired;
01158 }
01159 
01160 static void reset_volumes(struct ast_conf_user *user)
01161 {
01162    signed char zero_volume = 0;
01163 
01164    ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
01165    ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
01166 }
01167 
01168 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
01169 {
01170    unsigned char *data;
01171    int len;
01172    int res = -1;
01173 
01174    ast_test_suite_event_notify("CONFPLAY", "Channel: %s\r\n"
01175       "Conference: %s\r\n"
01176       "Marked: %d",
01177       ast_channel_name(chan),
01178       conf->confno,
01179       conf->markedusers);
01180 
01181    if (!ast_check_hangup(chan))
01182       res = ast_autoservice_start(chan);
01183 
01184    AST_LIST_LOCK(&confs);
01185 
01186    switch(sound) {
01187    case ENTER:
01188       data = enter;
01189       len = sizeof(enter);
01190       break;
01191    case LEAVE:
01192       data = leave;
01193       len = sizeof(leave);
01194       break;
01195    default:
01196       data = NULL;
01197       len = 0;
01198    }
01199    if (data) {
01200       careful_write(conf->fd, data, len, 1);
01201    }
01202 
01203    AST_LIST_UNLOCK(&confs);
01204 
01205    if (!res) 
01206       ast_autoservice_stop(chan);
01207 }
01208 
01209 static int user_no_cmp(void *obj, void *arg, int flags)
01210 {
01211    struct ast_conf_user *user = obj;
01212    int *user_no = arg;
01213 
01214    if (user->user_no == *user_no) {
01215       return (CMP_MATCH | CMP_STOP);
01216    }
01217 
01218    return 0;
01219 }
01220 
01221 static int user_max_cmp(void *obj, void *arg, int flags)
01222 {
01223    struct ast_conf_user *user = obj;
01224    int *max_no = arg;
01225 
01226    if (user->user_no > *max_no) {
01227       *max_no = user->user_no;
01228    }
01229 
01230    return 0;
01231 }
01232 
01233 /*!
01234  * \brief Find or create a conference
01235  *
01236  * \param confno The conference name/number
01237  * \param pin The regular user pin
01238  * \param pinadmin The admin pin
01239  * \param make Make the conf if it doesn't exist
01240  * \param dynamic Mark the newly created conference as dynamic
01241  * \param refcount How many references to mark on the conference
01242  * \param chan The asterisk channel
01243  *
01244  * \return A pointer to the conference struct, or NULL if it wasn't found and
01245  *         make or dynamic were not set.
01246  */
01247 static struct ast_conference *build_conf(const char *confno, const char *pin,
01248    const char *pinadmin, int make, int dynamic, int refcount,
01249    const struct ast_channel *chan, struct ast_test *test)
01250 {
01251    struct ast_conference *cnf;
01252    struct dahdi_confinfo dahdic = { 0, };
01253    int confno_int = 0;
01254    struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
01255    struct ast_format tmp_fmt;
01256 
01257    AST_LIST_LOCK(&confs);
01258 
01259    AST_LIST_TRAVERSE(&confs, cnf, list) {
01260       if (!strcmp(confno, cnf->confno)) 
01261          break;
01262    }
01263 
01264    if (cnf || (!make && !dynamic) || !cap_slin)
01265       goto cnfout;
01266 
01267    ast_format_cap_add(cap_slin, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0));
01268    /* Make a new one */
01269    if (!(cnf = ast_calloc(1, sizeof(*cnf))) ||
01270       !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) {
01271       goto cnfout;
01272    }
01273 
01274    ast_mutex_init(&cnf->playlock);
01275    ast_mutex_init(&cnf->listenlock);
01276    cnf->recordthread = AST_PTHREADT_NULL;
01277    ast_mutex_init(&cnf->recordthreadlock);
01278    cnf->announcethread = AST_PTHREADT_NULL;
01279    ast_mutex_init(&cnf->announcethreadlock);
01280    ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
01281    ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
01282    ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
01283    ast_copy_string(cnf->uniqueid, ast_channel_uniqueid(chan), sizeof(cnf->uniqueid));
01284 
01285    /* Setup a new dahdi conference */
01286    dahdic.confno = -1;
01287    dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
01288    cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
01289    if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
01290       if (test) {
01291          /* if we are creating a conference for a unit test, it is not neccesary
01292           * to open a pseudo channel, so, if we fail continue creating
01293           * the conference. */
01294          ast_test_status_update(test, "Unable to open DAHDI pseudo device\n");
01295       } else {
01296          ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
01297          if (cnf->fd >= 0)
01298             close(cnf->fd);
01299          ao2_ref(cnf->usercontainer, -1);
01300          ast_mutex_destroy(&cnf->playlock);
01301          ast_mutex_destroy(&cnf->listenlock);
01302          ast_mutex_destroy(&cnf->recordthreadlock);
01303          ast_mutex_destroy(&cnf->announcethreadlock);
01304          ast_free(cnf);
01305          cnf = NULL;
01306          goto cnfout;
01307       }
01308    }
01309 
01310    cnf->dahdiconf = dahdic.confno;
01311 
01312    /* Setup a new channel for playback of audio files */
01313    cnf->chan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL);
01314    if (cnf->chan) {
01315       ast_set_read_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
01316       ast_set_write_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
01317       dahdic.chan = 0;
01318       dahdic.confno = cnf->dahdiconf;
01319       dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
01320       if (ioctl(ast_channel_fd(cnf->chan, 0), DAHDI_SETCONF, &dahdic)) {
01321          if (test) {
01322             ast_test_status_update(test, "Error setting conference on pseudo channel\n");
01323          }
01324          ast_log(LOG_WARNING, "Error setting conference\n");
01325          if (cnf->chan)
01326             ast_hangup(cnf->chan);
01327          else
01328             close(cnf->fd);
01329          ao2_ref(cnf->usercontainer, -1);
01330          ast_mutex_destroy(&cnf->playlock);
01331          ast_mutex_destroy(&cnf->listenlock);
01332          ast_mutex_destroy(&cnf->recordthreadlock);
01333          ast_mutex_destroy(&cnf->announcethreadlock);
01334          ast_free(cnf);
01335          cnf = NULL;
01336          goto cnfout;
01337       }
01338    }
01339 
01340    /* Fill the conference struct */
01341    cnf->start = time(NULL);
01342    cnf->maxusers = 0x7fffffff;
01343    cnf->isdynamic = dynamic ? 1 : 0;
01344    ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
01345    AST_LIST_INSERT_HEAD(&confs, cnf, list);
01346 
01347    /* Reserve conference number in map */
01348    if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
01349       conf_map[confno_int] = 1;
01350    
01351 cnfout:
01352    cap_slin = ast_format_cap_destroy(cap_slin);
01353    if (cnf)
01354       ast_atomic_fetchadd_int(&cnf->refcount, refcount);
01355 
01356    AST_LIST_UNLOCK(&confs);
01357 
01358    return cnf;
01359 }
01360 
01361 static char *complete_confno(const char *word, int state)
01362 {
01363    struct ast_conference *cnf;
01364    char *ret = NULL;
01365    int which = 0;
01366    int len = strlen(word);
01367 
01368    AST_LIST_LOCK(&confs);
01369    AST_LIST_TRAVERSE(&confs, cnf, list) {
01370       if (!strncmp(word, cnf->confno, len) && ++which > state) {
01371          /* dup before releasing the lock */
01372          ret = ast_strdup(cnf->confno);
01373          break;
01374       }
01375    }
01376    AST_LIST_UNLOCK(&confs);
01377    return ret;
01378 }
01379 
01380 static char *complete_userno(struct ast_conference *cnf, const char *word, int state)
01381 {
01382    char usrno[50];
01383    struct ao2_iterator iter;
01384    struct ast_conf_user *usr;
01385    char *ret = NULL;
01386    int which = 0;
01387    int len = strlen(word);
01388 
01389    iter = ao2_iterator_init(cnf->usercontainer, 0);
01390    for (; (usr = ao2_iterator_next(&iter)); ao2_ref(usr, -1)) {
01391       snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
01392       if (!strncmp(word, usrno, len) && ++which > state) {
01393          ao2_ref(usr, -1);
01394          ret = ast_strdup(usrno);
01395          break;
01396       }
01397    }
01398    ao2_iterator_destroy(&iter);
01399    return ret;
01400 }
01401 
01402 static char *complete_meetmecmd_mute_kick(const char *line, const char *word, int pos, int state)
01403 {
01404    if (pos == 2) {
01405       return complete_confno(word, state);
01406    }
01407    if (pos == 3) {
01408       int len = strlen(word);
01409       char *ret = NULL;
01410       char *saved = NULL;
01411       char *myline;
01412       char *confno;
01413       struct ast_conference *cnf;
01414 
01415       if (!strncasecmp(word, "all", len)) {
01416          if (state == 0) {
01417             return ast_strdup("all");
01418          }
01419          --state;
01420       }
01421 
01422       /* Extract the confno from the command line. */
01423       myline = ast_strdupa(line);
01424       strtok_r(myline, " ", &saved);
01425       strtok_r(NULL, " ", &saved);
01426       confno = strtok_r(NULL, " ", &saved);
01427 
01428       AST_LIST_LOCK(&confs);
01429       AST_LIST_TRAVERSE(&confs, cnf, list) {
01430          if (!strcmp(confno, cnf->confno)) {
01431             ret = complete_userno(cnf, word, state);
01432             break;
01433          }
01434       }
01435       AST_LIST_UNLOCK(&confs);
01436 
01437       return ret;
01438    }
01439    return NULL;
01440 }
01441 
01442 static char *complete_meetmecmd_lock(const char *word, int pos, int state)
01443 {
01444    if (pos == 2) {
01445       return complete_confno(word, state);
01446    }
01447    return NULL;
01448 }
01449 
01450 static char *complete_meetmecmd_list(const char *line, const char *word, int pos, int state)
01451 {
01452    int len;
01453 
01454    if (pos == 2) {
01455       len = strlen(word);
01456       if (!strncasecmp(word, STR_CONCISE, len)) {
01457          if (state == 0) {
01458             return ast_strdup(STR_CONCISE);
01459          }
01460          --state;
01461       }
01462 
01463       return complete_confno(word, state);
01464    }
01465    if (pos == 3 && state == 0) {
01466       char *saved = NULL;
01467       char *myline;
01468       char *confno;
01469 
01470       /* Extract the confno from the command line. */
01471       myline = ast_strdupa(line);
01472       strtok_r(myline, " ", &saved);
01473       strtok_r(NULL, " ", &saved);
01474       confno = strtok_r(NULL, " ", &saved);
01475 
01476       if (!strcasecmp(confno, STR_CONCISE)) {
01477          /* There is nothing valid in this position now. */
01478          return NULL;
01479       }
01480 
01481       len = strlen(word);
01482       if (!strncasecmp(word, STR_CONCISE, len)) {
01483          return ast_strdup(STR_CONCISE);
01484       }
01485    }
01486    return NULL;
01487 }
01488 
01489 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01490 {
01491    /* Process the command */
01492    struct ast_conf_user *user;
01493    struct ast_conference *cnf;
01494    int hr, min, sec;
01495    int total = 0;
01496    time_t now;
01497 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s  %-8s  %-6s\n"
01498 #define MC_DATA_FORMAT "%-12.12s   %4.4d        %4.4s       %02d:%02d:%02d  %-8s  %-6s\n"
01499 
01500    switch (cmd) {
01501    case CLI_INIT:
01502       e->command = "meetme list";
01503       e->usage =
01504          "Usage: meetme list [<confno>] [" STR_CONCISE "]\n"
01505          "       List all conferences or a specific conference.\n";
01506       return NULL;
01507    case CLI_GENERATE:
01508       return complete_meetmecmd_list(a->line, a->word, a->pos, a->n);
01509    }
01510 
01511    if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], STR_CONCISE))) {
01512       /* List all the conferences */
01513       int concise = (a->argc == 3);
01514       struct ast_str *marked_users;
01515 
01516       if (!(marked_users = ast_str_create(30))) {
01517          return CLI_FAILURE;
01518       }
01519 
01520       now = time(NULL);
01521       AST_LIST_LOCK(&confs);
01522       if (AST_LIST_EMPTY(&confs)) {
01523          if (!concise) {
01524             ast_cli(a->fd, "No active MeetMe conferences.\n");
01525          }
01526          AST_LIST_UNLOCK(&confs);
01527          ast_free(marked_users);
01528          return CLI_SUCCESS;
01529       }
01530       if (!concise) {
01531          ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
01532       }
01533       AST_LIST_TRAVERSE(&confs, cnf, list) {
01534          hr = (now - cnf->start) / 3600;
01535          min = ((now - cnf->start) % 3600) / 60;
01536          sec = (now - cnf->start) % 60;
01537          if (!concise) {
01538             if (cnf->markedusers == 0) {
01539                ast_str_set(&marked_users, 0, "N/A ");
01540             } else {
01541                ast_str_set(&marked_users, 0, "%4.4d", cnf->markedusers);
01542             }
01543             ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users,
01544                ast_str_buffer(marked_users), hr, min, sec,
01545                cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
01546          } else {
01547             ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
01548                cnf->confno,
01549                cnf->users,
01550                cnf->markedusers,
01551                hr, min, sec,
01552                cnf->isdynamic,
01553                cnf->locked);
01554          }
01555 
01556          total += cnf->users;
01557       }
01558       AST_LIST_UNLOCK(&confs);
01559       if (!concise) {
01560          ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
01561       }
01562       ast_free(marked_users);
01563       return CLI_SUCCESS;
01564    }
01565    if (a->argc == 3 || (a->argc == 4 && !strcasecmp(a->argv[3], STR_CONCISE))) {
01566       struct ao2_iterator user_iter;
01567       int concise = (a->argc == 4);
01568 
01569       /* List all the users in a conference */
01570       if (AST_LIST_EMPTY(&confs)) {
01571          if (!concise) {
01572             ast_cli(a->fd, "No active MeetMe conferences.\n");
01573          }
01574          return CLI_SUCCESS;
01575       }
01576       /* Find the right conference */
01577       AST_LIST_LOCK(&confs);
01578       AST_LIST_TRAVERSE(&confs, cnf, list) {
01579          if (strcmp(cnf->confno, a->argv[2]) == 0) {
01580             break;
01581          }
01582       }
01583       if (!cnf) {
01584          if (!concise)
01585             ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
01586          AST_LIST_UNLOCK(&confs);
01587          return CLI_SUCCESS;
01588       }
01589       /* Show all the users */
01590       time(&now);
01591       user_iter = ao2_iterator_init(cnf->usercontainer, 0);
01592       while((user = ao2_iterator_next(&user_iter))) {
01593          hr = (now - user->jointime) / 3600;
01594          min = ((now - user->jointime) % 3600) / 60;
01595          sec = (now - user->jointime) % 60;
01596          if (!concise) {
01597             ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
01598                user->user_no,
01599                S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
01600                S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
01601                ast_channel_name(user->chan),
01602                ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
01603                ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
01604                user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
01605                user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
01606                istalking(user->talking), hr, min, sec); 
01607          } else {
01608             ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
01609                user->user_no,
01610                S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, ""),
01611                S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, ""),
01612                ast_channel_name(user->chan),
01613                ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
01614                ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
01615                user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
01616                user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
01617                user->talking, hr, min, sec);
01618          }
01619          ao2_ref(user, -1);
01620       }
01621       ao2_iterator_destroy(&user_iter);
01622       if (!concise) {
01623          ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
01624       }
01625       AST_LIST_UNLOCK(&confs);
01626       return CLI_SUCCESS;
01627    }
01628    return CLI_SHOWUSAGE;
01629 }
01630 
01631 
01632 static char *meetme_cmd_helper(struct ast_cli_args *a)
01633 {
01634    /* Process the command */
01635    struct ast_str *cmdline;
01636 
01637    /* Max confno length */
01638    if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
01639       return CLI_FAILURE;
01640    }
01641 
01642    ast_str_set(&cmdline, 0, "%s", a->argv[2]);  /* Argv 2: conference number */
01643    if (strcasestr(a->argv[1], "lock")) {
01644       if (strcasecmp(a->argv[1], "lock") == 0) {
01645          /* Lock */
01646          ast_str_append(&cmdline, 0, ",L");
01647       } else {
01648          /* Unlock */
01649          ast_str_append(&cmdline, 0, ",l");
01650       }
01651    } else if (strcasestr(a->argv[1], "mute")) { 
01652       if (strcasecmp(a->argv[1], "mute") == 0) {
01653          /* Mute */
01654          if (strcasecmp(a->argv[3], "all") == 0) {
01655             ast_str_append(&cmdline, 0, ",N");
01656          } else {
01657             ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);  
01658          }
01659       } else {
01660          /* Unmute */
01661          if (strcasecmp(a->argv[3], "all") == 0) {
01662             ast_str_append(&cmdline, 0, ",n");
01663          } else {
01664             ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
01665          }
01666       }
01667    } else if (strcasecmp(a->argv[1], "kick") == 0) {
01668       if (strcasecmp(a->argv[3], "all") == 0) {
01669          /* Kick all */
01670          ast_str_append(&cmdline, 0, ",K");
01671       } else {
01672          /* Kick a single user */
01673          ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
01674       }
01675    } else {
01676       /*
01677        * Should never get here because it is already filtered by the
01678        * callers.
01679        */
01680       ast_free(cmdline);
01681       return CLI_SHOWUSAGE;
01682    }
01683 
01684    ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
01685 
01686    admin_exec(NULL, ast_str_buffer(cmdline));
01687    ast_free(cmdline);
01688 
01689    return CLI_SUCCESS;
01690 }
01691 
01692 static char *meetme_lock_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01693 {
01694    switch (cmd) {
01695    case CLI_INIT:
01696       e->command = "meetme {lock|unlock}";
01697       e->usage =
01698          "Usage: meetme lock|unlock <confno>\n"
01699          "       Lock or unlock a conference to new users.\n";
01700       return NULL;
01701    case CLI_GENERATE:
01702       return complete_meetmecmd_lock(a->word, a->pos, a->n);
01703    }
01704 
01705    if (a->argc != 3) {
01706       return CLI_SHOWUSAGE;
01707    }
01708 
01709    return meetme_cmd_helper(a);
01710 }
01711 
01712 static char *meetme_kick_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01713 {
01714    switch (cmd) {
01715    case CLI_INIT:
01716       e->command = "meetme kick";
01717       e->usage =
01718          "Usage: meetme kick <confno> all|<userno>\n"
01719          "       Kick a conference or a user in a conference.\n";
01720       return NULL;
01721    case CLI_GENERATE:
01722       return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
01723    }
01724 
01725    if (a->argc != 4) {
01726       return CLI_SHOWUSAGE;
01727    }
01728 
01729    return meetme_cmd_helper(a);
01730 }
01731 
01732 static char *meetme_mute_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01733 {
01734    switch (cmd) {
01735    case CLI_INIT:
01736       e->command = "meetme {mute|unmute}";
01737       e->usage =
01738          "Usage: meetme mute|unmute <confno> all|<userno>\n"
01739          "       Mute or unmute a conference or a user in a conference.\n";
01740       return NULL;
01741    case CLI_GENERATE:
01742       return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
01743    }
01744 
01745    if (a->argc != 4) {
01746       return CLI_SHOWUSAGE;
01747    }
01748 
01749    return meetme_cmd_helper(a);
01750 }
01751 
01752 static const char *sla_hold_str(unsigned int hold_access)
01753 {
01754    const char *hold = "Unknown";
01755 
01756    switch (hold_access) {
01757    case SLA_HOLD_OPEN:
01758       hold = "Open";
01759       break;
01760    case SLA_HOLD_PRIVATE:
01761       hold = "Private";
01762    default:
01763       break;
01764    }
01765 
01766    return hold;
01767 }
01768 
01769 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01770 {
01771    struct ao2_iterator i;
01772    struct sla_trunk *trunk;
01773 
01774    switch (cmd) {
01775    case CLI_INIT:
01776       e->command = "sla show trunks";
01777       e->usage =
01778          "Usage: sla show trunks\n"
01779          "       This will list all trunks defined in sla.conf\n";
01780       return NULL;
01781    case CLI_GENERATE:
01782       return NULL;
01783    }
01784 
01785    ast_cli(a->fd, "\n"
01786                "=============================================================\n"
01787                "=== Configured SLA Trunks ===================================\n"
01788                "=============================================================\n"
01789                "===\n");
01790    i = ao2_iterator_init(sla_trunks, 0);
01791    for (; (trunk = ao2_iterator_next(&i)); ao2_ref(trunk, -1)) {
01792       struct sla_station_ref *station_ref;
01793       char ring_timeout[16] = "(none)";
01794 
01795       ao2_lock(trunk);
01796 
01797       if (trunk->ring_timeout) {
01798          snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
01799       }
01800 
01801       ast_cli(a->fd, "=== ---------------------------------------------------------\n"
01802                   "=== Trunk Name:       %s\n"
01803                   "=== ==> Device:       %s\n"
01804                   "=== ==> AutoContext:  %s\n"
01805                   "=== ==> RingTimeout:  %s\n"
01806                   "=== ==> BargeAllowed: %s\n"
01807                   "=== ==> HoldAccess:   %s\n"
01808                   "=== ==> Stations ...\n",
01809                   trunk->name, trunk->device, 
01810                   S_OR(trunk->autocontext, "(none)"), 
01811                   ring_timeout,
01812                   trunk->barge_disabled ? "No" : "Yes",
01813                   sla_hold_str(trunk->hold_access));
01814 
01815       AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
01816          ast_cli(a->fd, "===    ==> Station name: %s\n", station_ref->station->name);
01817       }
01818 
01819       ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
01820 
01821       ao2_unlock(trunk);
01822    }
01823    ao2_iterator_destroy(&i);
01824    ast_cli(a->fd, "=============================================================\n\n");
01825 
01826    return CLI_SUCCESS;
01827 }
01828 
01829 static const char *trunkstate2str(enum sla_trunk_state state)
01830 {
01831 #define S(e) case e: return # e;
01832    switch (state) {
01833    S(SLA_TRUNK_STATE_IDLE)
01834    S(SLA_TRUNK_STATE_RINGING)
01835    S(SLA_TRUNK_STATE_UP)
01836    S(SLA_TRUNK_STATE_ONHOLD)
01837    S(SLA_TRUNK_STATE_ONHOLD_BYME)
01838    }
01839    return "Uknown State";
01840 #undef S
01841 }
01842 
01843 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01844 {
01845    struct ao2_iterator i;
01846    struct sla_station *station;
01847 
01848    switch (cmd) {
01849    case CLI_INIT:
01850       e->command = "sla show stations";
01851       e->usage =
01852          "Usage: sla show stations\n"
01853          "       This will list all stations defined in sla.conf\n";
01854       return NULL;
01855    case CLI_GENERATE:
01856       return NULL;
01857    }
01858 
01859    ast_cli(a->fd, "\n" 
01860                "=============================================================\n"
01861                "=== Configured SLA Stations =================================\n"
01862                "=============================================================\n"
01863                "===\n");
01864    i = ao2_iterator_init(sla_stations, 0);
01865    for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
01866       struct sla_trunk_ref *trunk_ref;
01867       char ring_timeout[16] = "(none)";
01868       char ring_delay[16] = "(none)";
01869 
01870       ao2_lock(station);
01871 
01872       if (station->ring_timeout) {
01873          snprintf(ring_timeout, sizeof(ring_timeout), 
01874             "%u", station->ring_timeout);
01875       }
01876       if (station->ring_delay) {
01877          snprintf(ring_delay, sizeof(ring_delay), 
01878             "%u", station->ring_delay);
01879       }
01880       ast_cli(a->fd, "=== ---------------------------------------------------------\n"
01881                   "=== Station Name:    %s\n"
01882                   "=== ==> Device:      %s\n"
01883                   "=== ==> AutoContext: %s\n"
01884                   "=== ==> RingTimeout: %s\n"
01885                   "=== ==> RingDelay:   %s\n"
01886                   "=== ==> HoldAccess:  %s\n"
01887                   "=== ==> Trunks ...\n",
01888                   station->name, station->device,
01889                   S_OR(station->autocontext, "(none)"), 
01890                   ring_timeout, ring_delay,
01891                   sla_hold_str(station->hold_access));
01892       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
01893          if (trunk_ref->ring_timeout) {
01894             snprintf(ring_timeout, sizeof(ring_timeout),
01895                "%u", trunk_ref->ring_timeout);
01896          } else
01897             strcpy(ring_timeout, "(none)");
01898          if (trunk_ref->ring_delay) {
01899             snprintf(ring_delay, sizeof(ring_delay),
01900                "%u", trunk_ref->ring_delay);
01901          } else
01902             strcpy(ring_delay, "(none)");
01903             ast_cli(a->fd, "===    ==> Trunk Name: %s\n"
01904                      "===       ==> State:       %s\n"
01905                      "===       ==> RingTimeout: %s\n"
01906                      "===       ==> RingDelay:   %s\n",
01907                      trunk_ref->trunk->name,
01908                      trunkstate2str(trunk_ref->state),
01909                      ring_timeout, ring_delay);
01910       }
01911       ast_cli(a->fd, "=== ---------------------------------------------------------\n"
01912                   "===\n");
01913 
01914       ao2_unlock(station);
01915    }
01916    ao2_iterator_destroy(&i);
01917    ast_cli(a->fd, "============================================================\n"
01918                "\n");
01919 
01920    return CLI_SUCCESS;
01921 }
01922 
01923 static struct ast_cli_entry cli_meetme[] = {
01924    AST_CLI_DEFINE(meetme_kick_cmd, "Kick a conference or a user in a conference."),
01925    AST_CLI_DEFINE(meetme_show_cmd, "List all conferences or a specific conference."),
01926    AST_CLI_DEFINE(meetme_lock_cmd, "Lock or unlock a conference to new users."),
01927    AST_CLI_DEFINE(meetme_mute_cmd, "Mute or unmute a conference or a user in a conference."),
01928    AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
01929    AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
01930 };
01931 
01932 static void conf_flush(int fd, struct ast_channel *chan)
01933 {
01934    int x;
01935 
01936    /* read any frames that may be waiting on the channel
01937       and throw them away
01938    */
01939    if (chan) {
01940       struct ast_frame *f;
01941 
01942       /* when no frames are available, this will wait
01943          for 1 millisecond maximum
01944       */
01945       while (ast_waitfor(chan, 1) > 0) {
01946          f = ast_read(chan);
01947          if (f)
01948             ast_frfree(f);
01949          else /* channel was hung up or something else happened */
01950             break;
01951       }
01952    }
01953 
01954    /* flush any data sitting in the pseudo channel */
01955    x = DAHDI_FLUSH_ALL;
01956    if (ioctl(fd, DAHDI_FLUSH, &x))
01957       ast_log(LOG_WARNING, "Error flushing channel\n");
01958 
01959 }
01960 
01961 /*! \brief Remove the conference from the list and free it.
01962 
01963    We assume that this was called while holding conflock. */
01964 static int conf_free(struct ast_conference *conf)
01965 {
01966    int x;
01967    struct announce_listitem *item;
01968    
01969    AST_LIST_REMOVE(&confs, conf, list);
01970    /*** DOCUMENTATION
01971       <managerEventInstance>
01972          <synopsis>Raised when a MeetMe conference ends.</synopsis>
01973          <syntax>
01974             <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
01975          </syntax>
01976          <see-also>
01977             <ref type="managerEvent">MeetmeJoin</ref>
01978          </see-also>
01979       </managerEventInstance>
01980    ***/
01981    manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
01982 
01983    if (conf->recording == MEETME_RECORD_ACTIVE) {
01984       conf->recording = MEETME_RECORD_TERMINATE;
01985       AST_LIST_UNLOCK(&confs);
01986       while (1) {
01987          usleep(1);
01988          AST_LIST_LOCK(&confs);
01989          if (conf->recording == MEETME_RECORD_OFF)
01990             break;
01991          AST_LIST_UNLOCK(&confs);
01992       }
01993    }
01994 
01995    for (x = 0; x < AST_FRAME_BITS; x++) {
01996       if (conf->transframe[x])
01997          ast_frfree(conf->transframe[x]);
01998       if (conf->transpath[x])
01999          ast_translator_free_path(conf->transpath[x]);
02000    }
02001    if (conf->announcethread != AST_PTHREADT_NULL) {
02002       ast_mutex_lock(&conf->announcelistlock);
02003       conf->announcethread_stop = 1;
02004       ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
02005       ast_cond_signal(&conf->announcelist_addition);
02006       ast_mutex_unlock(&conf->announcelistlock);
02007       pthread_join(conf->announcethread, NULL);
02008    
02009       while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
02010          /* If it's a voicemail greeting file we don't want to remove it */
02011          if (!item->vmrec){
02012             ast_filedelete(item->namerecloc, NULL);
02013          }
02014          ao2_ref(item, -1);
02015       }
02016       ast_mutex_destroy(&conf->announcelistlock);
02017    }
02018 
02019    if (conf->origframe)
02020       ast_frfree(conf->origframe);
02021    if (conf->lchan)
02022       ast_hangup(conf->lchan);
02023    if (conf->chan)
02024       ast_hangup(conf->chan);
02025    if (conf->fd >= 0)
02026       close(conf->fd);
02027    if (conf->recordingfilename) {
02028       ast_free(conf->recordingfilename);
02029    }
02030    if (conf->usercontainer) {
02031       ao2_ref(conf->usercontainer, -1);
02032    }
02033    if (conf->recordingformat) {
02034       ast_free(conf->recordingformat);
02035    }
02036    ast_mutex_destroy(&conf->playlock);
02037    ast_mutex_destroy(&conf->listenlock);
02038    ast_mutex_destroy(&conf->recordthreadlock);
02039    ast_mutex_destroy(&conf->announcethreadlock);
02040    ast_free(conf);
02041 
02042    return 0;
02043 }
02044 
02045 static void conf_queue_dtmf(const struct ast_conference *conf,
02046    const struct ast_conf_user *sender, struct ast_frame *f)
02047 {
02048    struct ast_conf_user *user;
02049    struct ao2_iterator user_iter;
02050 
02051    user_iter = ao2_iterator_init(conf->usercontainer, 0);
02052    while ((user = ao2_iterator_next(&user_iter))) {
02053       if (user == sender) {
02054          ao2_ref(user, -1);
02055          continue;
02056       }
02057       if (ast_write(user->chan, f) < 0)
02058          ast_log(LOG_WARNING, "Error writing frame to channel %s\n", ast_channel_name(user->chan));
02059       ao2_ref(user, -1);
02060    }
02061    ao2_iterator_destroy(&user_iter);
02062 }
02063 
02064 static void sla_queue_event_full(enum sla_event_type type, 
02065    struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
02066 {
02067    struct sla_event *event;
02068 
02069    if (sla.thread == AST_PTHREADT_NULL) {
02070       ao2_ref(station, -1);
02071       ao2_ref(trunk_ref, -1);
02072       return;
02073    }
02074 
02075    if (!(event = ast_calloc(1, sizeof(*event)))) {
02076       ao2_ref(station, -1);
02077       ao2_ref(trunk_ref, -1);
02078       return;
02079    }
02080 
02081    event->type = type;
02082    event->trunk_ref = trunk_ref;
02083    event->station = station;
02084 
02085    if (!lock) {
02086       AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
02087       return;
02088    }
02089 
02090    ast_mutex_lock(&sla.lock);
02091    AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
02092    ast_cond_signal(&sla.cond);
02093    ast_mutex_unlock(&sla.lock);
02094 }
02095 
02096 static void sla_queue_event_nolock(enum sla_event_type type)
02097 {
02098    sla_queue_event_full(type, NULL, NULL, 0);
02099 }
02100 
02101 static void sla_queue_event(enum sla_event_type type)
02102 {
02103    sla_queue_event_full(type, NULL, NULL, 1);
02104 }
02105 
02106 /*! \brief Queue a SLA event from the conference */
02107 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
02108    struct ast_conference *conf)
02109 {
02110    struct sla_station *station;
02111    struct sla_trunk_ref *trunk_ref = NULL;
02112    char *trunk_name;
02113    struct ao2_iterator i;
02114 
02115    trunk_name = ast_strdupa(conf->confno);
02116    strsep(&trunk_name, "_");
02117    if (ast_strlen_zero(trunk_name)) {
02118       ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
02119       return;
02120    }
02121 
02122    i = ao2_iterator_init(sla_stations, 0);
02123    while ((station = ao2_iterator_next(&i))) {
02124       ao2_lock(station);
02125       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
02126          if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name)) {
02127             ao2_ref(trunk_ref, 1);
02128             break;
02129          }
02130       }
02131       ao2_unlock(station);
02132       if (trunk_ref) {
02133          /* station reference given to sla_queue_event_full() */
02134          break;
02135       }
02136       ao2_ref(station, -1);
02137    }
02138    ao2_iterator_destroy(&i);
02139 
02140    if (!trunk_ref) {
02141       ast_debug(1, "Trunk not found for event!\n");
02142       return;
02143    }
02144 
02145    sla_queue_event_full(type, trunk_ref, station, 1);
02146 }
02147 
02148 /*! \brief Decrement reference counts, as incremented by find_conf() */
02149 static int dispose_conf(struct ast_conference *conf)
02150 {
02151    int res = 0;
02152    int confno_int = 0;
02153 
02154    AST_LIST_LOCK(&confs);
02155    if (ast_atomic_dec_and_test(&conf->refcount)) {
02156       /* Take the conference room number out of an inuse state */
02157       if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
02158          conf_map[confno_int] = 0;
02159       }
02160       conf_free(conf);
02161       res = 1;
02162    }
02163    AST_LIST_UNLOCK(&confs);
02164 
02165    return res;
02166 }
02167 
02168 static int rt_extend_conf(const char *confno)
02169 {
02170    char currenttime[32];
02171    char endtime[32];
02172    struct timeval now;
02173    struct ast_tm tm;
02174    struct ast_variable *var, *orig_var;
02175    char bookid[51];
02176 
02177    if (!extendby) {
02178       return 0;
02179    }
02180 
02181    now = ast_tvnow();
02182 
02183    ast_localtime(&now, &tm, NULL);
02184    ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
02185 
02186    var = ast_load_realtime("meetme", "confno",
02187       confno, "startTime<= ", currenttime,
02188       "endtime>= ", currenttime, NULL);
02189 
02190    orig_var = var;
02191 
02192    /* Identify the specific RealTime conference */
02193    while (var) {
02194       if (!strcasecmp(var->name, "bookid")) {
02195          ast_copy_string(bookid, var->value, sizeof(bookid));
02196       }
02197       if (!strcasecmp(var->name, "endtime")) {
02198          ast_copy_string(endtime, var->value, sizeof(endtime));
02199       }
02200 
02201       var = var->next;
02202    }
02203    ast_variables_destroy(orig_var);
02204 
02205    ast_strptime(endtime, DATE_FORMAT, &tm);
02206    now = ast_mktime(&tm, NULL);
02207 
02208    now.tv_sec += extendby;
02209 
02210    ast_localtime(&now, &tm, NULL);
02211    ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
02212    strcat(currenttime, "0"); /* Seconds needs to be 00 */
02213 
02214    var = ast_load_realtime("meetme", "confno",
02215       confno, "startTime<= ", currenttime,
02216       "endtime>= ", currenttime, NULL);
02217 
02218    /* If there is no conflict with extending the conference, update the DB */
02219    if (!var) {
02220       ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
02221       ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
02222       return 0;
02223 
02224    }
02225 
02226    ast_variables_destroy(var);
02227    return -1;
02228 }
02229 
02230 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
02231 {
02232    char *original_moh;
02233 
02234    ast_channel_lock(chan);
02235    original_moh = ast_strdupa(ast_channel_musicclass(chan));
02236    ast_channel_musicclass_set(chan, musicclass);
02237    ast_channel_unlock(chan);
02238 
02239    ast_moh_start(chan, original_moh, NULL);
02240 
02241    ast_channel_lock(chan);
02242    ast_channel_musicclass_set(chan, original_moh);
02243    ast_channel_unlock(chan);
02244 }
02245 
02246 static const char *get_announce_filename(enum announcetypes type)
02247 {
02248    switch (type) {
02249    case CONF_HASLEFT:
02250       return "conf-hasleft";
02251       break;
02252    case CONF_HASJOIN:
02253       return "conf-hasjoin";
02254       break;
02255    default:
02256       return "";
02257    }
02258 }
02259 
02260 static void *announce_thread(void *data)
02261 {
02262    struct announce_listitem *current;
02263    struct ast_conference *conf = data;
02264    int res;
02265    char filename[PATH_MAX] = "";
02266    AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
02267    AST_LIST_HEAD_INIT_NOLOCK(&local_list);
02268 
02269    while (!conf->announcethread_stop) {
02270       ast_mutex_lock(&conf->announcelistlock);
02271       if (conf->announcethread_stop) {
02272          ast_mutex_unlock(&conf->announcelistlock);
02273          break;
02274       }
02275       if (AST_LIST_EMPTY(&conf->announcelist))
02276          ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
02277 
02278       AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
02279       AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
02280 
02281       ast_mutex_unlock(&conf->announcelistlock);
02282       if (conf->announcethread_stop) {
02283          break;
02284       }
02285 
02286       for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
02287          ast_debug(1, "About to play %s\n", current->namerecloc);
02288          if (!ast_fileexists(current->namerecloc, NULL, NULL))
02289             continue;
02290          if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
02291             if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
02292                res = ast_waitstream(current->confchan, "");
02293             if (!res) {
02294                ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
02295                if (!ast_streamfile(current->confchan, filename, current->language))
02296                   ast_waitstream(current->confchan, "");
02297             }
02298          }
02299          if (current->announcetype == CONF_HASLEFT && current->announcetype && !current->vmrec) {
02300             /* only remove it if it isn't a VM recording file */
02301             ast_filedelete(current->namerecloc, NULL);
02302          }
02303       }
02304    }
02305 
02306    /* thread marked to stop, clean up */
02307    while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
02308       /* only delete if it's a vm rec */
02309       if (!current->vmrec) {
02310          ast_filedelete(current->namerecloc, NULL);
02311       }
02312       ao2_ref(current, -1);
02313    }
02314    return NULL;
02315 }
02316 
02317 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
02318 {
02319    if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
02320       return 1;
02321    }
02322 
02323    return (ast_channel_state(chan) == AST_STATE_UP);
02324 }
02325 
02326 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
02327 {
02328    /*** DOCUMENTATION
02329       <managerEventInstance>
02330          <synopsis>Raised when a MeetMe user begins or ends talking.</synopsis>
02331          <syntax>
02332             <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
02333             <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Usernum'])" />
02334             <parameter name="Status">
02335                <enumlist>
02336                   <enum name="on"/>
02337                   <enum name="off"/>
02338                </enumlist>
02339             </parameter>
02340          </syntax>
02341       </managerEventInstance>
02342    ***/
02343    ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalking",
02344       "Channel: %s\r\n"
02345       "Uniqueid: %s\r\n"
02346       "Meetme: %s\r\n"
02347       "Usernum: %d\r\n"
02348       "Status: %s\r\n",
02349       ast_channel_name(chan), ast_channel_uniqueid(chan),
02350       conf->confno,
02351       user->user_no, talking ? "on" : "off");
02352 }
02353 
02354 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
02355 {
02356    int last_talking = user->talking;
02357    if (last_talking == talking)
02358       return;
02359 
02360    user->talking = talking;
02361 
02362    if (monitor) {
02363       /* Check if talking state changed. Take care of -1 which means unmonitored */
02364       int was_talking = (last_talking > 0);
02365       int now_talking = (talking > 0);
02366       if (was_talking != now_talking) {
02367          send_talking_event(chan, conf, user, now_talking);
02368       }
02369    }
02370 }
02371 
02372 static int user_set_hangup_cb(void *obj, void *check_admin_arg, int flags)
02373 {
02374    struct ast_conf_user *user = obj;
02375    /* actual pointer contents of check_admin_arg is irrelevant */
02376 
02377    if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
02378       user->adminflags |= ADMINFLAG_HANGUP;
02379    }
02380    return 0;
02381 }
02382 
02383 static int user_set_kickme_cb(void *obj, void *check_admin_arg, int flags)
02384 {
02385    struct ast_conf_user *user = obj;
02386    /* actual pointer contents of check_admin_arg is irrelevant */
02387 
02388    if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
02389       user->adminflags |= ADMINFLAG_KICKME;
02390    }
02391    return 0;
02392 }
02393 
02394 static int user_set_unmuted_cb(void *obj, void *check_admin_arg, int flags)
02395 {
02396    struct ast_conf_user *user = obj;
02397    /* actual pointer contents of check_admin_arg is irrelevant */
02398 
02399    if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
02400       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
02401    }
02402    return 0;
02403 }
02404 
02405 static int user_set_muted_cb(void *obj, void *check_admin_arg, int flags)
02406 {
02407    struct ast_conf_user *user = obj;
02408    /* actual pointer contents of check_admin_arg is irrelevant */
02409 
02410    if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
02411       user->adminflags |= ADMINFLAG_MUTED;
02412    }
02413    return 0;
02414 }
02415 
02416 enum menu_modes {
02417    MENU_DISABLED = 0,
02418    MENU_NORMAL,
02419    MENU_ADMIN,
02420    MENU_ADMIN_EXTENDED,
02421 };
02422 
02423 /*! \internal
02424  * \brief Processes menu options for the standard menu (accessible through the 's' option for app_meetme)
02425  *
02426  * \param menu_mode a pointer to the currently active menu_mode.
02427  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
02428  * \param conf the active conference for which the user has called the menu from.
02429  * \param confflags flags used by conf for various options
02430  * \param chan ast_channel belonging to the user who called the menu
02431  * \param user which meetme conference user invoked the menu
02432  */
02433 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)
02434 {
02435    switch (*dtmf) {
02436    case '1': /* Un/Mute */
02437       *menu_mode = MENU_DISABLED;
02438 
02439       /* user can only toggle the self-muted state */
02440       user->adminflags ^= ADMINFLAG_SELFMUTED;
02441 
02442       /* they can't override the admin mute state */
02443       if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
02444          if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
02445             ast_waitstream(chan, "");
02446          }
02447       } else {
02448          if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
02449             ast_waitstream(chan, "");
02450          }
02451       }
02452       break;
02453 
02454    case '2':
02455       *menu_mode = MENU_DISABLED;
02456       if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
02457          user->adminflags |= ADMINFLAG_T_REQUEST;
02458       }
02459 
02460       if (user->adminflags & ADMINFLAG_T_REQUEST) {
02461          if (!ast_streamfile(chan, "beep", ast_channel_language(chan))) {
02462             ast_waitstream(chan, "");
02463          }
02464       }
02465       break;
02466 
02467    case '4':
02468       tweak_listen_volume(user, VOL_DOWN);
02469       break;
02470    case '5':
02471       /* Extend RT conference */
02472       if (rt_schedule) {
02473          rt_extend_conf(conf->confno);
02474       }
02475       *menu_mode = MENU_DISABLED;
02476       break;
02477 
02478    case '6':
02479       tweak_listen_volume(user, VOL_UP);
02480       break;
02481 
02482    case '7':
02483       tweak_talk_volume(user, VOL_DOWN);
02484       break;
02485 
02486    case '8':
02487       *menu_mode = MENU_DISABLED;
02488       break;
02489 
02490    case '9':
02491       tweak_talk_volume(user, VOL_UP);
02492       break;
02493 
02494    default:
02495       *menu_mode = MENU_DISABLED;
02496       if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02497          ast_waitstream(chan, "");
02498       }
02499       break;
02500    }
02501 }
02502 
02503 /*! \internal
02504  * \brief Processes menu options for the adminstrator menu (accessible through the 's' option for app_meetme)
02505  *
02506  * \param menu_mode a pointer to the currently active menu_mode.
02507  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
02508  * \param conf the active conference for which the user has called the menu from.
02509  * \param confflags flags used by conf for various options
02510  * \param chan ast_channel belonging to the user who called the menu
02511  * \param user which meetme conference user invoked the menu
02512  */
02513 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)
02514 {
02515    switch(*dtmf) {
02516    case '1': /* Un/Mute */
02517       *menu_mode = MENU_DISABLED;
02518       /* for admin, change both admin and use flags */
02519       if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
02520          user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
02521       } else {
02522          user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
02523       }
02524 
02525       if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
02526          if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
02527             ast_waitstream(chan, "");
02528          }
02529       } else {
02530          if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
02531             ast_waitstream(chan, "");
02532          }
02533       }
02534       break;
02535 
02536    case '2': /* Un/Lock the Conference */
02537       *menu_mode = MENU_DISABLED;
02538       if (conf->locked) {
02539          conf->locked = 0;
02540          if (!ast_streamfile(chan, "conf-unlockednow", ast_channel_language(chan))) {
02541             ast_waitstream(chan, "");
02542          }
02543       } else {
02544          conf->locked = 1;
02545          if (!ast_streamfile(chan, "conf-lockednow", ast_channel_language(chan))) {
02546             ast_waitstream(chan, "");
02547          }
02548       }
02549       break;
02550 
02551    case '3': /* Eject last user */
02552    {
02553       struct ast_conf_user *usr = NULL;
02554       int max_no = 0;
02555       ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
02556       *menu_mode = MENU_DISABLED;
02557       usr = ao2_find(conf->usercontainer, &max_no, 0);
02558       if ((ast_channel_name(usr->chan) == ast_channel_name(chan)) || ast_test_flag64(&usr->userflags, CONFFLAG_ADMIN)) {
02559          if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02560             ast_waitstream(chan, "");
02561          }
02562       } else {
02563          usr->adminflags |= ADMINFLAG_KICKME;
02564       }
02565       ao2_ref(usr, -1);
02566       ast_stopstream(chan);
02567       break;
02568    }
02569 
02570    case '4':
02571       tweak_listen_volume(user, VOL_DOWN);
02572       break;
02573 
02574    case '5':
02575       /* Extend RT conference */
02576       if (rt_schedule) {
02577          if (!rt_extend_conf(conf->confno)) {
02578             if (!ast_streamfile(chan, "conf-extended", ast_channel_language(chan))) {
02579                ast_waitstream(chan, "");
02580             }
02581          } else {
02582             if (!ast_streamfile(chan, "conf-nonextended", ast_channel_language(chan))) {
02583                ast_waitstream(chan, "");
02584             }
02585          }
02586          ast_stopstream(chan);
02587       }
02588       *menu_mode = MENU_DISABLED;
02589       break;
02590 
02591    case '6':
02592       tweak_listen_volume(user, VOL_UP);
02593       break;
02594 
02595    case '7':
02596       tweak_talk_volume(user, VOL_DOWN);
02597       break;
02598 
02599    case '8':
02600       if (!ast_streamfile(chan, "conf-adminmenu-menu8", ast_channel_language(chan))) {
02601          /* 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. */
02602          *dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
02603          ast_stopstream(chan);
02604       }
02605       *menu_mode = MENU_ADMIN_EXTENDED;
02606       break;
02607 
02608    case '9':
02609       tweak_talk_volume(user, VOL_UP);
02610       break;
02611    default:
02612       menu_mode = MENU_DISABLED;
02613       /* Play an error message! */
02614       if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02615          ast_waitstream(chan, "");
02616       }
02617       break;
02618    }
02619 
02620 }
02621 
02622 /*! \internal
02623  * \brief Processes menu options for the extended administrator menu (accessible through option 8 on the administrator menu)
02624  *
02625  * \param menu_mode a pointer to the currently active menu_mode.
02626  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
02627  * \param conf the active conference for which the user has called the menu from.
02628  * \param confflags flags used by conf for various options
02629  * \param chan ast_channel belonging to the user who called the menu
02630  * \param user which meetme conference user invoked the menu
02631  * \param recordingtmp character buffer which may hold the name of the conference recording file
02632  * \param dahdic dahdi configuration info used by the main conference loop
02633  */
02634 
02635 static void meetme_menu_admin_extended(enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size, struct dahdi_confinfo *dahdic, struct ast_format_cap *cap_slin)
02636 {
02637    int keepplaying;
02638    int playednamerec;
02639    int res;
02640    struct ao2_iterator user_iter;
02641    struct ast_conf_user *usr = NULL;
02642 
02643    switch(*dtmf) {
02644    case '1': /* *81 Roll call */
02645       keepplaying = 1;
02646       playednamerec = 0;
02647       if (conf->users == 1) {
02648          if (keepplaying && !ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
02649             res = ast_waitstream(chan, AST_DIGIT_ANY);
02650             ast_stopstream(chan);
02651             if (res > 0) {
02652                keepplaying = 0;
02653             }
02654          }
02655       } else if (conf->users == 2) {
02656          if (keepplaying && !ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
02657             res = ast_waitstream(chan, AST_DIGIT_ANY);
02658             ast_stopstream(chan);
02659             if (res > 0) {
02660                keepplaying = 0;
02661             }
02662          }
02663       } else {
02664          if (keepplaying && !ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
02665             res = ast_waitstream(chan, AST_DIGIT_ANY);
02666             ast_stopstream(chan);
02667             if (res > 0) {
02668                keepplaying = 0;
02669             }
02670          }
02671          if (keepplaying) {
02672             res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
02673             ast_stopstream(chan);
02674             if (res > 0) {
02675                keepplaying = 0;
02676             }
02677          }
02678          if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
02679             res = ast_waitstream(chan, AST_DIGIT_ANY);
02680             ast_stopstream(chan);
02681             if (res > 0) {
02682                keepplaying = 0;
02683             }
02684          }
02685       }
02686       user_iter = ao2_iterator_init(conf->usercontainer, 0);
02687       while((usr = ao2_iterator_next(&user_iter))) {
02688          if (ast_fileexists(usr->namerecloc, NULL, NULL)) {
02689             if (keepplaying && !ast_streamfile(chan, usr->namerecloc, ast_channel_language(chan))) {
02690                res = ast_waitstream(chan, AST_DIGIT_ANY);
02691                ast_stopstream(chan);
02692                if (res > 0) {
02693                   keepplaying = 0;
02694                }
02695             }
02696             playednamerec = 1;
02697          }
02698          ao2_ref(usr, -1);
02699       }
02700       ao2_iterator_destroy(&user_iter);
02701       if (keepplaying && playednamerec && !ast_streamfile(chan, "conf-roll-callcomplete", ast_channel_language(chan))) {
02702          res = ast_waitstream(chan, AST_DIGIT_ANY);
02703          ast_stopstream(chan);
02704          if (res > 0) {
02705             keepplaying = 0;
02706          }
02707       }
02708 
02709       *menu_mode = MENU_DISABLED;
02710       break;
02711 
02712    case '2': /* *82 Eject all non-admins */
02713       if (conf->users == 1) {
02714          if(!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02715             ast_waitstream(chan, "");
02716          }
02717       } else {
02718          ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_kickme_cb, &conf);
02719       }
02720       ast_stopstream(chan);
02721       *menu_mode = MENU_DISABLED;
02722       break;
02723 
02724    case '3': /* *83 (Admin) mute/unmute all non-admins */
02725       if(conf->gmuted) {
02726          conf->gmuted = 0;
02727          ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, &conf);
02728          if (!ast_streamfile(chan, "conf-now-unmuted", ast_channel_language(chan))) {
02729             ast_waitstream(chan, "");
02730          }
02731       } else {
02732          conf->gmuted = 1;
02733          ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_muted_cb, &conf);
02734          if (!ast_streamfile(chan, "conf-now-muted", ast_channel_language(chan))) {
02735             ast_waitstream(chan, "");
02736          }
02737       }
02738       ast_stopstream(chan);
02739       *menu_mode = MENU_DISABLED;
02740       break;
02741 
02742    case '4': /* *84 Record conference */
02743       if (conf->recording != MEETME_RECORD_ACTIVE) {
02744          ast_set_flag64(confflags, CONFFLAG_RECORDCONF);
02745          if (!conf->recordingfilename) {
02746             const char *var;
02747             ast_channel_lock(chan);
02748             if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
02749                conf->recordingfilename = ast_strdup(var);
02750             }
02751             if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
02752                conf->recordingformat = ast_strdup(var);
02753             }
02754             ast_channel_unlock(chan);
02755             if (!conf->recordingfilename) {
02756                snprintf(recordingtmp, recordingtmp_size, "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
02757                conf->recordingfilename = ast_strdup(recordingtmp);
02758             }
02759             if (!conf->recordingformat) {
02760                conf->recordingformat = ast_strdup("wav");
02761             }
02762             ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
02763             conf->confno, conf->recordingfilename, conf->recordingformat);
02764          }
02765 
02766          ast_mutex_lock(&conf->recordthreadlock);
02767          if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) {
02768             ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
02769             ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
02770             dahdic->chan = 0;
02771             dahdic->confno = conf->dahdiconf;
02772             dahdic->confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
02773             if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, dahdic)) {
02774                ast_log(LOG_WARNING, "Error starting listen channel\n");
02775                ast_hangup(conf->lchan);
02776                conf->lchan = NULL;
02777             } else {
02778                ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
02779             }
02780          }
02781          ast_mutex_unlock(&conf->recordthreadlock);
02782          if (!ast_streamfile(chan, "conf-now-recording", ast_channel_language(chan))) {
02783             ast_waitstream(chan, "");
02784          }
02785       }
02786 
02787       ast_stopstream(chan);
02788       *menu_mode = MENU_DISABLED;
02789       break;
02790 
02791    case '8': /* *88 Exit the menu and return to the conference... without an error message */
02792       ast_stopstream(chan);
02793       *menu_mode = MENU_DISABLED;
02794       break;
02795 
02796    default:
02797       if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02798          ast_waitstream(chan, "");
02799       }
02800       ast_stopstream(chan);
02801       *menu_mode = MENU_DISABLED;
02802       break;
02803    }
02804 }
02805 
02806 /*! \internal
02807  * \brief Processes menu options for the various menu types (accessible through the 's' option for app_meetme)
02808  *
02809  * \param menu_mode a pointer to the currently active menu_mode.
02810  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
02811  * \param conf the active conference for which the user has called the menu from.
02812  * \param confflags flags used by conf for various options
02813  * \param chan ast_channel belonging to the user who called the menu
02814  * \param user which meetme conference user invoked the menu
02815  * \param recordingtmp character buffer which may hold the name of the conference recording file
02816  * \param dahdic dahdi configuration info used by the main conference loop
02817  */
02818 
02819 static void meetme_menu(enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size, struct dahdi_confinfo *dahdic, struct ast_format_cap *cap_slin)
02820 {
02821    switch (*menu_mode) {
02822    case MENU_DISABLED:
02823       break;
02824    case MENU_NORMAL:
02825       meetme_menu_normal(menu_mode, dtmf, conf, confflags, chan, user);
02826       break;
02827    case MENU_ADMIN:
02828       meetme_menu_admin(menu_mode, dtmf, conf, confflags, chan, user);
02829       /* Admin Menu is capable of branching into another menu, in which case it will reset dtmf and change the menu mode. */
02830       if (*menu_mode != MENU_ADMIN_EXTENDED || (*dtmf <= 0)) {
02831          break;
02832       }
02833    case MENU_ADMIN_EXTENDED:
02834       meetme_menu_admin_extended(menu_mode, dtmf, conf, confflags, chan, user, recordingtmp, recordingtmp_size, dahdic, cap_slin);
02835       break;
02836    }
02837 }
02838 
02839 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
02840 {
02841    struct ast_conf_user *user = NULL;
02842    int fd;
02843    struct dahdi_confinfo dahdic, dahdic_empty;
02844    struct ast_frame *f;
02845    struct ast_channel *c;
02846    struct ast_frame fr;
02847    int outfd;
02848    int ms;
02849    int nfds;
02850    int res;
02851    int retrydahdi;
02852    int origfd;
02853    int musiconhold = 0, mohtempstopped = 0;
02854    int firstpass = 0;
02855    int lastmarked = 0;
02856    int currentmarked = 0;
02857    int ret = -1;
02858    int x;
02859    enum menu_modes menu_mode = MENU_DISABLED;
02860    int talkreq_manager = 0;
02861    int using_pseudo = 0;
02862    int duration = 20;
02863    int sent_event = 0;
02864    int checked = 0;
02865    int announcement_played = 0;
02866    struct timeval now;
02867    struct ast_dsp *dsp = NULL;
02868    struct ast_app *agi_app;
02869    char *agifile, *mod_speex;
02870    const char *agifiledefault = "conf-background.agi", *tmpvar;
02871    char meetmesecs[30] = "";
02872    char exitcontext[AST_MAX_CONTEXT] = "";
02873    char recordingtmp[AST_MAX_EXTENSION] = "";
02874    char members[10] = "";
02875    int dtmf = 0, opt_waitmarked_timeout = 0;
02876    time_t timeout = 0;
02877    struct dahdi_bufferinfo bi;
02878    char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
02879    char *buf = __buf + AST_FRIENDLY_OFFSET;
02880    char *exitkeys = NULL;
02881    unsigned int calldurationlimit = 0;
02882    long timelimit = 0;
02883    long play_warning = 0;
02884    long warning_freq = 0;
02885    const char *warning_sound = NULL;
02886    const char *end_sound = NULL;
02887    char *parse;
02888    long time_left_ms = 0;
02889    struct timeval nexteventts = { 0, };
02890    int to;
02891    int setusercount = 0;
02892    int confsilence = 0, totalsilence = 0;
02893    char *mailbox, *context;
02894    struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
02895    struct ast_format tmpfmt;
02896 
02897    if (!cap_slin) {
02898       goto conf_run_cleanup;
02899    }
02900    ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
02901 
02902    if (!(user = ao2_alloc(sizeof(*user), NULL))) {
02903       goto conf_run_cleanup;
02904    }
02905 
02906    /* Possible timeout waiting for marked user */
02907    if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
02908       !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
02909       (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
02910       (opt_waitmarked_timeout > 0)) {
02911       timeout = time(NULL) + opt_waitmarked_timeout;
02912    }
02913 
02914    if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
02915       calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
02916       ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
02917    }
02918 
02919    if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
02920       char *limit_str, *warning_str, *warnfreq_str;
02921       const char *var;
02922 
02923       parse = optargs[OPT_ARG_DURATION_LIMIT];
02924       limit_str = strsep(&parse, ":");
02925       warning_str = strsep(&parse, ":");
02926       warnfreq_str = parse;
02927 
02928       timelimit = atol(limit_str);
02929       if (warning_str)
02930          play_warning = atol(warning_str);
02931       if (warnfreq_str)
02932          warning_freq = atol(warnfreq_str);
02933 
02934       if (!timelimit) {
02935          timelimit = play_warning = warning_freq = 0;
02936          warning_sound = NULL;
02937       } else if (play_warning > timelimit) {
02938          if (!warning_freq) {
02939             play_warning = 0;
02940          } else {
02941             while (play_warning > timelimit)
02942                play_warning -= warning_freq;
02943             if (play_warning < 1)
02944                play_warning = warning_freq = 0;
02945          }
02946       }
02947 
02948       ast_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
02949       if (play_warning) {
02950          ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
02951       }
02952       if (warning_freq) {
02953          ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
02954       }
02955 
02956       ast_channel_lock(chan);
02957       if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
02958          var = ast_strdupa(var);
02959       }
02960       ast_channel_unlock(chan);
02961 
02962       warning_sound = var ? var : "timeleft";
02963 
02964       ast_channel_lock(chan);
02965       if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
02966          var = ast_strdupa(var);
02967       }
02968       ast_channel_unlock(chan);
02969 
02970       end_sound = var ? var : NULL;
02971 
02972       /* undo effect of S(x) in case they are both used */
02973       calldurationlimit = 0;
02974       /* more efficient do it like S(x) does since no advanced opts */
02975       if (!play_warning && !end_sound && timelimit) {
02976          calldurationlimit = timelimit / 1000;
02977          timelimit = play_warning = warning_freq = 0;
02978       } else {
02979          ast_debug(2, "Limit Data for this call:\n");
02980          ast_debug(2, "- timelimit     = %ld\n", timelimit);
02981          ast_debug(2, "- play_warning  = %ld\n", play_warning);
02982          ast_debug(2, "- warning_freq  = %ld\n", warning_freq);
02983          ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
02984          ast_debug(2, "- end_sound     = %s\n", end_sound ? end_sound : "UNDEF");
02985       }
02986    }
02987 
02988    /* Get exit keys */
02989    if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
02990       if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
02991          exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
02992       else
02993          exitkeys = ast_strdupa("#"); /* Default */
02994    }
02995    
02996    if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
02997       if (!conf->recordingfilename) {
02998          const char *var;
02999          ast_channel_lock(chan);
03000          if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
03001             conf->recordingfilename = ast_strdup(var);
03002          }
03003          if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
03004             conf->recordingformat = ast_strdup(var);
03005          }
03006          ast_channel_unlock(chan);
03007          if (!conf->recordingfilename) {
03008             snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
03009             conf->recordingfilename = ast_strdup(recordingtmp);
03010          }
03011          if (!conf->recordingformat) {
03012             conf->recordingformat = ast_strdup("wav");
03013          }
03014          ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
03015                 conf->confno, conf->recordingfilename, conf->recordingformat);
03016       }
03017    }
03018 
03019    ast_mutex_lock(&conf->recordthreadlock);
03020    if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
03021       ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) {
03022       ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
03023       ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
03024       dahdic.chan = 0;
03025       dahdic.confno = conf->dahdiconf;
03026       dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
03027       if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) {
03028          ast_log(LOG_WARNING, "Error starting listen channel\n");
03029          ast_hangup(conf->lchan);
03030          conf->lchan = NULL;
03031       } else {
03032          ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
03033       }
03034    }
03035    ast_mutex_unlock(&conf->recordthreadlock);
03036 
03037    ast_mutex_lock(&conf->announcethreadlock);
03038    if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03039       ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) {
03040       ast_mutex_init(&conf->announcelistlock);
03041       AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
03042       ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
03043    }
03044    ast_mutex_unlock(&conf->announcethreadlock);
03045 
03046    time(&user->jointime);
03047    
03048    user->timelimit = timelimit;
03049    user->play_warning = play_warning;
03050    user->warning_freq = warning_freq;
03051    user->warning_sound = warning_sound;
03052    user->end_sound = end_sound;  
03053    
03054    if (calldurationlimit > 0) {
03055       time(&user->kicktime);
03056       user->kicktime = user->kicktime + calldurationlimit;
03057    }
03058    
03059    if (ast_tvzero(user->start_time))
03060       user->start_time = ast_tvnow();
03061    time_left_ms = user->timelimit;
03062    
03063    if (user->timelimit) {
03064       nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
03065       nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
03066    }
03067 
03068    if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
03069       /* Sorry, but this conference is locked! */  
03070       if (!ast_streamfile(chan, "conf-locked", ast_channel_language(chan)))
03071          ast_waitstream(chan, "");
03072       goto outrun;
03073    }
03074 
03075       ast_mutex_lock(&conf->playlock);
03076 
03077    if (rt_schedule && conf->maxusers) {
03078       if (conf->users >= conf->maxusers) {
03079          /* Sorry, but this confernce has reached the participant limit! */   
03080          if (!ast_streamfile(chan, "conf-full", ast_channel_language(chan)))
03081             ast_waitstream(chan, "");
03082          ast_mutex_unlock(&conf->playlock);
03083          goto outrun;
03084       }
03085    }
03086 
03087    ao2_lock(conf->usercontainer);
03088    ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
03089    user->user_no++;
03090    ao2_link(conf->usercontainer, user);
03091    ao2_unlock(conf->usercontainer);
03092 
03093    user->chan = chan;
03094    user->userflags = *confflags;
03095    user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
03096    user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
03097    user->talking = -1;
03098 
03099    ast_mutex_unlock(&conf->playlock);
03100 
03101    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC))) {
03102       char destdir[PATH_MAX];
03103 
03104       snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
03105 
03106       if (ast_mkdir(destdir, 0777) != 0) {
03107          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
03108          goto outrun;
03109       }
03110 
03111       if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
03112          context = ast_strdupa(optargs[OPT_ARG_INTROUSER_VMREC]);
03113          mailbox = strsep(&context, "@");
03114 
03115          if (ast_strlen_zero(mailbox)) {
03116             /* invalid input, clear the v flag*/
03117             ast_clear_flag64(confflags,CONFFLAG_INTROUSER_VMREC);
03118             ast_log(LOG_WARNING,"You must specify a mailbox in the v() option\n");
03119          } else {
03120             if (ast_strlen_zero(context)) {
03121                 context = "default";
03122             }
03123             /* if there is no mailbox we don't need to do this logic  */
03124             snprintf(user->namerecloc, sizeof(user->namerecloc),
03125                 "%s/voicemail/%s/%s/greet",ast_config_AST_SPOOL_DIR,context,mailbox);
03126 
03127             /* if the greeting doesn't exist then use the temp file method instead, clear flag v */
03128             if (!ast_fileexists(user->namerecloc, NULL, NULL)){
03129                snprintf(user->namerecloc, sizeof(user->namerecloc),
03130                    "%s/meetme-username-%s-%d", destdir,
03131                    conf->confno, user->user_no);
03132                ast_clear_flag64(confflags, CONFFLAG_INTROUSER_VMREC);
03133             }
03134          }
03135       } else {
03136          snprintf(user->namerecloc, sizeof(user->namerecloc),
03137              "%s/meetme-username-%s-%d", destdir,
03138              conf->confno, user->user_no);
03139       }
03140 
03141       res = 0;
03142       if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) && !ast_fileexists(user->namerecloc, NULL, NULL))
03143          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);
03144       else if (ast_test_flag64(confflags, CONFFLAG_INTROUSER) && !ast_fileexists(user->namerecloc, NULL, NULL))
03145          res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
03146       if (res == -1)
03147          goto outrun;
03148 
03149    }
03150 
03151    ast_mutex_lock(&conf->playlock);
03152 
03153    if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
03154       conf->markedusers++;
03155    conf->users++;
03156    if (rt_log_members) {
03157       /* Update table */
03158       snprintf(members, sizeof(members), "%d", conf->users);
03159       ast_realtime_require_field("meetme",
03160          "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
03161          "members", RQ_UINTEGER1, strlen(members),
03162          NULL);
03163       ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
03164    }
03165    setusercount = 1;
03166 
03167    /* This device changed state now - if this is the first user */
03168    if (conf->users == 1)
03169       ast_devstate_changed(AST_DEVICE_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
03170 
03171    ast_mutex_unlock(&conf->playlock);
03172 
03173    /* return the unique ID of the conference */
03174    pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
03175 
03176    if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
03177       ast_channel_lock(chan);
03178       if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
03179          ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
03180       } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))) {
03181          ast_copy_string(exitcontext, ast_channel_macrocontext(chan), sizeof(exitcontext));
03182       } else {
03183          ast_copy_string(exitcontext, ast_channel_context(chan), sizeof(exitcontext));
03184       }
03185       ast_channel_unlock(chan);
03186    }
03187 
03188    /* Play an arbitrary intro message */
03189    if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
03190          !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
03191       if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], ast_channel_language(chan))) {
03192          ast_waitstream(chan, "");
03193       }
03194    }
03195 
03196    if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
03197       if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
03198          if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan)))
03199             ast_waitstream(chan, "");
03200       if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
03201          if (!ast_streamfile(chan, "conf-waitforleader", ast_channel_language(chan)))
03202             ast_waitstream(chan, "");
03203    }
03204 
03205    if (ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
03206       int keepplaying = 1;
03207 
03208       if (conf->users == 2) { 
03209          if (!ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
03210             res = ast_waitstream(chan, AST_DIGIT_ANY);
03211             ast_stopstream(chan);
03212             if (res > 0)
03213                keepplaying = 0;
03214             else if (res == -1)
03215                goto outrun;
03216          }
03217       } else { 
03218          if (!ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
03219             res = ast_waitstream(chan, AST_DIGIT_ANY);
03220             ast_stopstream(chan);
03221             if (res > 0)
03222                keepplaying = 0;
03223             else if (res == -1)
03224                goto outrun;
03225          }
03226          if (keepplaying) {
03227             res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03228             if (res > 0)
03229                keepplaying = 0;
03230             else if (res == -1)
03231                goto outrun;
03232          }
03233          if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
03234             res = ast_waitstream(chan, AST_DIGIT_ANY);
03235             ast_stopstream(chan);
03236             if (res > 0)
03237                keepplaying = 0;
03238             else if (res == -1) 
03239                goto outrun;
03240          }
03241       }
03242    }
03243 
03244    if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
03245       /* We're leaving this alone until the state gets changed to up */
03246       ast_indicate(chan, -1);
03247    }
03248 
03249    if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
03250       ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", ast_channel_name(chan));
03251       goto outrun;
03252    }
03253 
03254    if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
03255       ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", ast_channel_name(chan));
03256       goto outrun;
03257    }
03258 
03259    /* Reduce background noise from each participant */
03260    if (!ast_test_flag64(confflags, CONFFLAG_DONT_DENOISE) &&
03261       (mod_speex = ast_module_helper("", "func_speex", 0, 0, 0, 0))) {
03262       ast_free(mod_speex);
03263       ast_func_write(chan, "DENOISE(rx)", "on");
03264    }
03265 
03266    retrydahdi = (strcasecmp(ast_channel_tech(chan)->type, "DAHDI") || (ast_channel_audiohooks(chan) || ast_channel_monitor(chan)) ? 1 : 0);
03267    user->dahdichannel = !retrydahdi;
03268 
03269  dahdiretry:
03270    origfd = ast_channel_fd(chan, 0);
03271    if (retrydahdi) {
03272       /* open pseudo in non-blocking mode */
03273       fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
03274       if (fd < 0) {
03275          ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
03276          goto outrun;
03277       }
03278       using_pseudo = 1;
03279       /* Setup buffering information */
03280       memset(&bi, 0, sizeof(bi));
03281       bi.bufsize = CONF_SIZE / 2;
03282       bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
03283       bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
03284       bi.numbufs = audio_buffers;
03285       if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
03286          ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
03287          close(fd);
03288          goto outrun;
03289       }
03290       x = 1;
03291       if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
03292          ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
03293          close(fd);
03294          goto outrun;
03295       }
03296       nfds = 1;
03297    } else {
03298       /* XXX Make sure we're not running on a pseudo channel XXX */
03299       fd = ast_channel_fd(chan, 0);
03300       nfds = 0;
03301    }
03302    memset(&dahdic, 0, sizeof(dahdic));
03303    memset(&dahdic_empty, 0, sizeof(dahdic_empty));
03304    /* Check to see if we're in a conference... */
03305    dahdic.chan = 0;
03306    if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
03307       ast_log(LOG_WARNING, "Error getting conference\n");
03308       close(fd);
03309       goto outrun;
03310    }
03311    if (dahdic.confmode) {
03312       /* Whoa, already in a conference...  Retry... */
03313       if (!retrydahdi) {
03314          ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
03315          retrydahdi = 1;
03316          goto dahdiretry;
03317       }
03318    }
03319    memset(&dahdic, 0, sizeof(dahdic));
03320    /* Add us to the conference */
03321    dahdic.chan = 0;
03322    dahdic.confno = conf->dahdiconf;
03323 
03324    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
03325          ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) || ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)) && conf->users > 1) {
03326       struct announce_listitem *item;
03327       if (!(item = ao2_alloc(sizeof(*item), NULL)))
03328          goto outrun;
03329       ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
03330       ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
03331       item->confchan = conf->chan;
03332       item->confusers = conf->users;
03333       if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
03334          item->vmrec = 1;
03335       }
03336       item->announcetype = CONF_HASJOIN;
03337       ast_mutex_lock(&conf->announcelistlock);
03338       ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
03339       AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
03340       ast_cond_signal(&conf->announcelist_addition);
03341       ast_mutex_unlock(&conf->announcelistlock);
03342 
03343       while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
03344          ;
03345       }
03346       ao2_ref(item, -1);
03347    }
03348 
03349    if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && !conf->markedusers)
03350       dahdic.confmode = DAHDI_CONF_CONF;
03351    else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
03352       dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
03353    else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
03354       dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
03355    else
03356       dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
03357 
03358    if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03359       ast_log(LOG_WARNING, "Error setting conference\n");
03360       close(fd);
03361       goto outrun;
03362    }
03363    ast_debug(1, "Placed channel %s in DAHDI conf %d\n", ast_channel_name(chan), conf->dahdiconf);
03364 
03365    if (!sent_event) {
03366       /*** DOCUMENTATION
03367          <managerEventInstance>
03368             <synopsis>Raised when a user joins a MeetMe conference.</synopsis>
03369             <syntax>
03370                <parameter name="Meetme">
03371                   <para>The identifier for the MeetMe conference.</para>
03372                </parameter>
03373                <parameter name="Usernum">
03374                   <para>The identifier of the MeetMe user who joined.</para>
03375                </parameter>
03376             </syntax>
03377             <see-also>
03378                <ref type="managerEvent">MeetmeLeave</ref>
03379                <ref type="application">MeetMe</ref>
03380             </see-also>
03381          </managerEventInstance>
03382       ***/
03383       ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
03384          "Channel: %s\r\n"
03385          "Uniqueid: %s\r\n"
03386          "Meetme: %s\r\n"
03387          "Usernum: %d\r\n"
03388          "CallerIDnum: %s\r\n"
03389          "CallerIDname: %s\r\n"
03390          "ConnectedLineNum: %s\r\n"
03391          "ConnectedLineName: %s\r\n",
03392          ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno,
03393          user->user_no,
03394          S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
03395          S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<unknown>"),
03396          S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, "<unknown>"),
03397          S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "<unknown>")
03398          );
03399       sent_event = 1;
03400    }
03401 
03402    if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
03403       !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03404       firstpass = 1;
03405       if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
03406          if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
03407             (conf->markedusers >= 1))) {
03408             conf_play(chan, conf, ENTER);
03409          }
03410    }
03411 
03412    conf_flush(fd, chan);
03413 
03414    if (dsp)
03415       ast_dsp_free(dsp);
03416 
03417    if (!(dsp = ast_dsp_new())) {
03418       ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
03419       res = -1;
03420    }
03421 
03422    if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
03423       /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
03424          or use default filename of conf-background.agi */
03425 
03426       ast_channel_lock(chan);
03427       if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
03428          agifile = ast_strdupa(tmpvar);
03429       } else {
03430          agifile = ast_strdupa(agifiledefault);
03431       }
03432       ast_channel_unlock(chan);
03433       
03434       if (user->dahdichannel) {
03435          /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
03436          x = 1;
03437          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03438       }
03439       /* Find a pointer to the agi app and execute the script */
03440       agi_app = pbx_findapp("agi");
03441       if (agi_app) {
03442          ret = pbx_exec(chan, agi_app, agifile);
03443       } else {
03444          ast_log(LOG_WARNING, "Could not find application (agi)\n");
03445          ret = -2;
03446       }
03447       if (user->dahdichannel) {
03448          /*  Remove CONFMUTE mode on DAHDI channel */
03449          x = 0;
03450          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03451       }
03452    } else {
03453       int lastusers = conf->users;
03454       if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
03455          /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
03456          x = 1;
03457          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03458       }
03459 
03460       for (;;) {
03461          int menu_was_active = 0;
03462 
03463          outfd = -1;
03464          ms = -1;
03465          now = ast_tvnow();
03466 
03467          if (rt_schedule && conf->endtime) {
03468             char currenttime[32];
03469             long localendtime = 0;
03470             int extended = 0;
03471             struct ast_tm tm;
03472             struct ast_variable *var, *origvar;
03473             struct timeval tmp;
03474 
03475             if (now.tv_sec % 60 == 0) {
03476                if (!checked) {
03477                   ast_localtime(&now, &tm, NULL);
03478                   ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
03479                   var = origvar = ast_load_realtime("meetme", "confno",
03480                      conf->confno, "starttime <=", currenttime,
03481                       "endtime >=", currenttime, NULL);
03482 
03483                   for ( ; var; var = var->next) {
03484                      if (!strcasecmp(var->name, "endtime")) {
03485                         struct ast_tm endtime_tm;
03486                         ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
03487                         tmp = ast_mktime(&endtime_tm, NULL);
03488                         localendtime = tmp.tv_sec;
03489                      }
03490                   }
03491                   ast_variables_destroy(origvar);
03492 
03493                   /* A conference can be extended from the
03494                      Admin/User menu or by an external source */
03495                   if (localendtime > conf->endtime){
03496                      conf->endtime = localendtime;
03497                      extended = 1;
03498                   }
03499 
03500                   if (conf->endtime && (now.tv_sec >= conf->endtime)) {
03501                      ast_verbose("Quitting time...\n");
03502                      goto outrun;
03503                   }
03504 
03505                   if (!announcement_played && conf->endalert) {
03506                      if (now.tv_sec + conf->endalert >= conf->endtime) {
03507                         if (!ast_streamfile(chan, "conf-will-end-in", ast_channel_language(chan)))
03508                            ast_waitstream(chan, "");
03509                         ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", ast_channel_language(chan));
03510                         if (!ast_streamfile(chan, "minutes", ast_channel_language(chan)))
03511                            ast_waitstream(chan, "");
03512                         if (musiconhold) {
03513                            conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03514                         }
03515                         announcement_played = 1;
03516                      }
03517                   }
03518 
03519                   if (extended) {
03520                      announcement_played = 0;
03521                   }
03522 
03523                   checked = 1;
03524                }
03525             } else {
03526                checked = 0;
03527             }
03528          }
03529 
03530          if (user->kicktime && (user->kicktime <= now.tv_sec)) {
03531             if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03532                ret = 0;
03533             } else {
03534                ret = -1;
03535             }
03536             break;
03537          }
03538   
03539          to = -1;
03540          if (user->timelimit) {
03541             int minutes = 0, seconds = 0, remain = 0;
03542  
03543             to = ast_tvdiff_ms(nexteventts, now);
03544             if (to < 0) {
03545                to = 0;
03546             }
03547             time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
03548             if (time_left_ms < to) {
03549                to = time_left_ms;
03550             }
03551    
03552             if (time_left_ms <= 0) {
03553                if (user->end_sound) {                 
03554                   res = ast_streamfile(chan, user->end_sound, ast_channel_language(chan));
03555                   res = ast_waitstream(chan, "");
03556                }
03557                if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03558                   ret = 0;
03559                } else {
03560                   ret = -1;
03561                }
03562                break;
03563             }
03564             
03565             if (!to) {
03566                if (time_left_ms >= 5000) {                  
03567                   
03568                   remain = (time_left_ms + 500) / 1000;
03569                   if (remain / 60 >= 1) {
03570                      minutes = remain / 60;
03571                      seconds = remain % 60;
03572                   } else {
03573                      seconds = remain;
03574                   }
03575                   
03576                   /* force the time left to round up if appropriate */
03577                   if (user->warning_sound && user->play_warning) {
03578                      if (!strcmp(user->warning_sound, "timeleft")) {
03579                         
03580                         res = ast_streamfile(chan, "vm-youhave", ast_channel_language(chan));
03581                         res = ast_waitstream(chan, "");
03582                         if (minutes) {
03583                            res = ast_say_number(chan, minutes, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03584                            res = ast_streamfile(chan, "queue-minutes", ast_channel_language(chan));
03585                            res = ast_waitstream(chan, "");
03586                         }
03587                         if (seconds) {
03588                            res = ast_say_number(chan, seconds, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03589                            res = ast_streamfile(chan, "queue-seconds", ast_channel_language(chan));
03590                            res = ast_waitstream(chan, "");
03591                         }
03592                      } else {
03593                         res = ast_streamfile(chan, user->warning_sound, ast_channel_language(chan));
03594                         res = ast_waitstream(chan, "");
03595                      }
03596                      if (musiconhold) {
03597                         conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03598                      }
03599                   }
03600                }
03601                if (user->warning_freq) {
03602                   nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
03603                } else {
03604                   nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
03605                }
03606             }
03607          }
03608 
03609          now = ast_tvnow();
03610          if (timeout && now.tv_sec >= timeout) {
03611             if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03612                ret = 0;
03613             } else {
03614                ret = -1;
03615             }
03616             break;
03617          }
03618 
03619          /* if we have just exited from the menu, and the user had a channel-driver
03620             volume adjustment, restore it
03621          */
03622          if (!menu_mode && menu_was_active && user->listen.desired && !user->listen.actual) {
03623             set_talk_volume(user, user->listen.desired);
03624          }
03625 
03626          menu_was_active = menu_mode;
03627 
03628          currentmarked = conf->markedusers;
03629          if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03630              ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
03631              ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
03632              lastmarked == 0) {
03633             if (currentmarked == 1 && conf->users > 1) {
03634                ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03635                if (conf->users - 1 == 1) {
03636                   if (!ast_streamfile(chan, "conf-userwilljoin", ast_channel_language(chan))) {
03637                      ast_waitstream(chan, "");
03638                   }
03639                } else {
03640                   if (!ast_streamfile(chan, "conf-userswilljoin", ast_channel_language(chan))) {
03641                      ast_waitstream(chan, "");
03642                   }
03643                }
03644             }
03645             if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
03646                if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
03647                   ast_waitstream(chan, "");
03648                }
03649             }
03650          }
03651 
03652          /* Update the struct with the actual confflags */
03653          user->userflags = *confflags;
03654 
03655          if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
03656             if (currentmarked == 0) {
03657                if (lastmarked != 0) {
03658                   if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
03659                      if (!ast_streamfile(chan, "conf-leaderhasleft", ast_channel_language(chan))) {
03660                         ast_waitstream(chan, "");
03661                      }
03662                   }
03663                   if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
03664                      if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03665                         ret = 0;
03666                      }
03667                      break;
03668                   } else {
03669                      dahdic.confmode = DAHDI_CONF_CONF;
03670                      if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03671                         ast_log(LOG_WARNING, "Error setting conference\n");
03672                         close(fd);
03673                         goto outrun;
03674                      }
03675                   }
03676                }
03677                if (!musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
03678                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03679                   musiconhold = 1;
03680                }
03681             } else if (currentmarked >= 1 && lastmarked == 0) {
03682                /* Marked user entered, so cancel timeout */
03683                timeout = 0;
03684                if (ast_test_flag64(confflags, CONFFLAG_MONITOR)) {
03685                   dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
03686                } else if (ast_test_flag64(confflags, CONFFLAG_TALKER)) {
03687                   dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
03688                } else {
03689                   dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
03690                }
03691                if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03692                   ast_log(LOG_WARNING, "Error setting conference\n");
03693                   close(fd);
03694                   goto outrun;
03695                }
03696                if (musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
03697                   ast_moh_stop(chan);
03698                   musiconhold = 0;
03699                }
03700                if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && 
03701                   !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
03702                   if (!ast_streamfile(chan, "conf-placeintoconf", ast_channel_language(chan))) {
03703                      ast_waitstream(chan, "");
03704                   }
03705                   conf_play(chan, conf, ENTER);
03706                }
03707             }
03708          }
03709 
03710          /* trying to add moh for single person conf */
03711          if (ast_test_flag64(confflags, CONFFLAG_MOH) && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
03712             if (conf->users == 1) {
03713                if (!musiconhold) {
03714                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03715                   musiconhold = 1;
03716                } 
03717             } else {
03718                if (musiconhold) {
03719                   ast_moh_stop(chan);
03720                   musiconhold = 0;
03721                }
03722             }
03723          }
03724          
03725          /* Leave if the last marked user left */
03726          if (currentmarked == 0 && lastmarked != 0 && ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
03727             if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03728                ret = 0;
03729             } else {
03730                ret = -1;
03731             }
03732             break;
03733          }
03734 
03735          /* Throw a TestEvent if a user exit did not cause this user to leave the conference */
03736          if (conf->users != lastusers) {
03737             if (conf->users < lastusers) {
03738                ast_test_suite_event_notify("NOEXIT", "Message: CONFFLAG_MARKEDEXIT\r\nLastUsers: %d\r\nUsers: %d", lastusers, conf->users);
03739             }
03740             lastusers = conf->users;
03741          }
03742 
03743          /* Check if my modes have changed */
03744 
03745          /* If I should be muted but am still talker, mute me */
03746          if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
03747             dahdic.confmode ^= DAHDI_CONF_TALKER;
03748             if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03749                ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
03750                ret = -1;
03751                break;
03752             }
03753 
03754             /* Indicate user is not talking anymore - change him to unmonitored state */
03755             if (ast_test_flag64(confflags,  (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER))) {
03756                set_user_talking(chan, conf, user, -1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03757             }
03758             /*** DOCUMENTATION
03759             <managerEventInstance>
03760                <synopsis>Raised when a MeetMe user is muted.</synopsis>
03761                <syntax>
03762                   <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
03763                   <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Usernum'])" />
03764                   <parameter name="Status">
03765                      <enumlist>
03766                         <enum name="on"/>
03767                         <enum name="off"/>
03768                      </enumlist>
03769                   </parameter>
03770                </syntax>
03771             </managerEventInstance>
03772             ***/
03773             ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
03774                "Channel: %s\r\n"
03775                "Uniqueid: %s\r\n"
03776                "Meetme: %s\r\n"
03777                "Usernum: %d\r\n"
03778                "Status: on\r\n",
03779                ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
03780          }
03781 
03782          /* If I should be un-muted but am not talker, un-mute me */
03783          if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
03784             dahdic.confmode |= DAHDI_CONF_TALKER;
03785             if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03786                ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
03787                ret = -1;
03788                break;
03789             }
03790             /*** DOCUMENTATION
03791             <managerEventInstance>
03792                <synopsis>Raised when a MeetMe user is unmuted.</synopsis>
03793             </managerEventInstance>
03794             ***/
03795             ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
03796                "Channel: %s\r\n"
03797                "Uniqueid: %s\r\n"
03798                "Meetme: %s\r\n"
03799                "Usernum: %d\r\n"
03800                "Status: off\r\n",
03801                ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
03802          }
03803          
03804          if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
03805             (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
03806             talkreq_manager = 1;
03807 
03808             /*** DOCUMENTATION
03809             <managerEventInstance>
03810                <synopsis>Raised when a MeetMe user has started talking.</synopsis>
03811                <syntax>
03812                   <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
03813                   <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Usernum'])" />
03814                   <parameter name="Status">
03815                      <enumlist>
03816                         <enum name="on"/>
03817                         <enum name="off"/>
03818                      </enumlist>
03819                   </parameter>
03820                </syntax>
03821             </managerEventInstance>
03822             ***/
03823             ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
03824                "Channel: %s\r\n"
03825                "Uniqueid: %s\r\n"
03826                "Meetme: %s\r\n"
03827                "Usernum: %d\r\n"
03828                "Status: on\r\n",
03829                ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
03830          }
03831 
03832          if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
03833             !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
03834             talkreq_manager = 0;
03835             /*** DOCUMENTATION
03836             <managerEventInstance>
03837                <synopsis>Raised when a MeetMe user has finished talking.</synopsis>
03838             </managerEventInstance>
03839             ***/
03840             ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
03841                "Channel: %s\r\n"
03842                "Uniqueid: %s\r\n"
03843                "Meetme: %s\r\n"
03844                "Usernum: %d\r\n"
03845                "Status: off\r\n",
03846                ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
03847          }
03848 
03849          /* If user have been hung up, exit the conference */
03850          if (user->adminflags & ADMINFLAG_HANGUP) {
03851             ret = 0;
03852             break;
03853          }
03854 
03855          /* If I have been kicked, exit the conference */
03856          if (user->adminflags & ADMINFLAG_KICKME) {
03857             /* You have been kicked. */
03858             if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && 
03859                !ast_streamfile(chan, "conf-kicked", ast_channel_language(chan))) {
03860                ast_waitstream(chan, "");
03861             }
03862             ret = 0;
03863             break;
03864          }
03865 
03866          /* Perform a hangup check here since ast_waitfor_nandfds will not always be able to get a channel after a hangup has occurred */
03867          if (ast_check_hangup(chan)) {
03868             break;
03869          }
03870 
03871          c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
03872 
03873          if (c) {
03874             char dtmfstr[2] = "";
03875 
03876             if (ast_channel_fd(c, 0) != origfd || (user->dahdichannel && (ast_channel_audiohooks(c) || ast_channel_monitor(c)))) {
03877                if (using_pseudo) {
03878                   /* Kill old pseudo */
03879                   close(fd);
03880                   using_pseudo = 0;
03881                }
03882                ast_debug(1, "Ooh, something swapped out under us, starting over\n");
03883                retrydahdi = (strcasecmp(ast_channel_tech(c)->type, "DAHDI") || (ast_channel_audiohooks(c) || ast_channel_monitor(c)) ? 1 : 0);
03884                user->dahdichannel = !retrydahdi;
03885                goto dahdiretry;
03886             }
03887             if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
03888                f = ast_read_noaudio(c);
03889             } else {
03890                f = ast_read(c);
03891             }
03892             if (!f) {
03893                break;
03894             }
03895             if (f->frametype == AST_FRAME_DTMF) {
03896                dtmfstr[0] = f->subclass.integer;
03897                dtmfstr[1] = '\0';
03898             }
03899 
03900             if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.format.id == AST_FORMAT_SLINEAR)) {
03901                if (user->talk.actual) {
03902                   ast_frame_adjust_volume(f, user->talk.actual);
03903                }
03904 
03905                if (ast_test_flag64(confflags, (CONFFLAG_OPTIMIZETALKER | CONFFLAG_MONITORTALKER))) {
03906                   if (user->talking == -1) {
03907                      user->talking = 0;
03908                   }
03909 
03910                   res = ast_dsp_silence(dsp, f, &totalsilence);
03911                   if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
03912                      set_user_talking(chan, conf, user, 1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03913                   }
03914 
03915                   if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
03916                      set_user_talking(chan, conf, user, 0, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03917                   }
03918                }
03919                if (using_pseudo) {
03920                   /* Absolutely do _not_ use careful_write here...
03921                      it is important that we read data from the channel
03922                      as fast as it arrives, and feed it into the conference.
03923                      The buffering in the pseudo channel will take care of any
03924                      timing differences, unless they are so drastic as to lose
03925                      audio frames (in which case carefully writing would only
03926                      have delayed the audio even further).
03927                   */
03928                   /* As it turns out, we do want to use careful write.  We just
03929                      don't want to block, but we do want to at least *try*
03930                      to write out all the samples.
03931                    */
03932                   if (user->talking || !ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER)) {
03933                      careful_write(fd, f->data.ptr, f->datalen, 0);
03934                   }
03935                }
03936             } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '*') && ast_test_flag64(confflags, CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_mode)) {
03937                if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03938                   conf_queue_dtmf(conf, user, f);
03939                }
03940                if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
03941                   ast_log(LOG_WARNING, "Error setting conference\n");
03942                   close(fd);
03943                   ast_frfree(f);
03944                   goto outrun;
03945                }
03946 
03947                /* if we are entering the menu, and the user has a channel-driver
03948                   volume adjustment, clear it
03949                */
03950                if (!menu_mode && user->talk.desired && !user->talk.actual) {
03951                   set_talk_volume(user, 0);
03952                }
03953 
03954                if (musiconhold) {
03955                   ast_moh_stop(chan);
03956                } else if (!menu_mode) {
03957                   char *menu_to_play;
03958                   if (ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03959                      menu_mode = MENU_ADMIN;
03960                      menu_to_play = "conf-adminmenu-18";
03961                   } else {
03962                      menu_mode = MENU_NORMAL;
03963                      menu_to_play = "conf-usermenu-162";
03964                   }
03965 
03966                   if (!ast_streamfile(chan, menu_to_play, ast_channel_language(chan))) {
03967                      dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
03968                      ast_stopstream(chan);
03969                   } else {
03970                      dtmf = 0;
03971                   }
03972                } else {
03973                   dtmf = f->subclass.integer;
03974                }
03975 
03976                if (dtmf > 0) {
03977                   meetme_menu(&menu_mode, &dtmf, conf, confflags, chan, user, recordingtmp, sizeof(recordingtmp), &dahdic, cap_slin);
03978                }
03979 
03980                if (musiconhold && !menu_mode) {
03981                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03982                }
03983 
03984                if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03985                   ast_log(LOG_WARNING, "Error setting conference\n");
03986                   close(fd);
03987                   ast_frfree(f);
03988                   goto outrun;
03989                }
03990 
03991                conf_flush(fd, chan);
03992             /*
03993              * Since options using DTMF could absorb DTMF meant for the
03994              * conference menu, we have to check them after the menu.
03995              */
03996             } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
03997                if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03998                   conf_queue_dtmf(conf, user, f);
03999                }
04000 
04001                if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
04002                   ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
04003                   ret = 0;
04004                   ast_frfree(f);
04005                   break;
04006                } else {
04007                   ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
04008                }
04009             } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_KEYEXIT) &&
04010                (strchr(exitkeys, f->subclass.integer))) {
04011                pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
04012 
04013                if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
04014                   conf_queue_dtmf(conf, user, f);
04015                }
04016                ret = 0;
04017                ast_frfree(f);
04018                break;
04019             } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
04020                && ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
04021                conf_queue_dtmf(conf, user, f);
04022             } else if (ast_test_flag64(confflags, CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
04023                switch (f->subclass.integer) {
04024                case AST_CONTROL_HOLD:
04025                   sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
04026                   break;
04027                default:
04028                   break;
04029                }
04030             } else if (f->frametype == AST_FRAME_NULL) {
04031                /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
04032             } else if (f->frametype == AST_FRAME_CONTROL) {
04033                switch (f->subclass.integer) {
04034                case AST_CONTROL_BUSY:
04035                case AST_CONTROL_CONGESTION:
04036                   ast_frfree(f);
04037                   goto outrun;
04038                   break;
04039                default:
04040                   ast_debug(1,
04041                      "Got ignored control frame on channel %s, f->frametype=%d,f->subclass=%d\n",
04042                      ast_channel_name(chan), f->frametype, f->subclass.integer);
04043                }
04044             } else {
04045                ast_debug(1,
04046                   "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
04047                   ast_channel_name(chan), f->frametype, f->subclass.integer);
04048             }
04049             ast_frfree(f);
04050          } else if (outfd > -1) {
04051             res = read(outfd, buf, CONF_SIZE);
04052             if (res > 0) {
04053                memset(&fr, 0, sizeof(fr));
04054                fr.frametype = AST_FRAME_VOICE;
04055                ast_format_set(&fr.subclass.format, AST_FORMAT_SLINEAR, 0);
04056                fr.datalen = res;
04057                fr.samples = res / 2;
04058                fr.data.ptr = buf;
04059                fr.offset = AST_FRIENDLY_OFFSET;
04060                if (!user->listen.actual &&
04061                   (ast_test_flag64(confflags, CONFFLAG_MONITOR) ||
04062                    (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
04063                    (!user->talking && ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER))
04064                    )) {
04065                   int idx;
04066                   for (idx = 0; idx < AST_FRAME_BITS; idx++) {
04067                      if (ast_format_to_old_bitfield(ast_channel_rawwriteformat(chan)) & (1 << idx)) {
04068                         break;
04069                      }
04070                   }
04071                   if (idx >= AST_FRAME_BITS) {
04072                      goto bailoutandtrynormal;
04073                   }
04074                   ast_mutex_lock(&conf->listenlock);
04075                   if (!conf->transframe[idx]) {
04076                      if (conf->origframe) {
04077                         if (musiconhold && !ast_dsp_silence(dsp, conf->origframe, &confsilence) && confsilence < MEETME_DELAYDETECTTALK) {
04078                            ast_moh_stop(chan);
04079                            mohtempstopped = 1;
04080                         }
04081                         if (!conf->transpath[idx]) {
04082                            struct ast_format src;
04083                            struct ast_format dst;
04084                            ast_format_set(&src, AST_FORMAT_SLINEAR, 0);
04085                            ast_format_from_old_bitfield(&dst, (1 << idx));
04086                            conf->transpath[idx] = ast_translator_build_path(&dst, &src);
04087                         }
04088                         if (conf->transpath[idx]) {
04089                            conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
04090                            if (!conf->transframe[idx]) {
04091                               conf->transframe[idx] = &ast_null_frame;
04092                            }
04093                         }
04094                      }
04095                   }
04096                   if (conf->transframe[idx]) {
04097                      if ((conf->transframe[idx]->frametype != AST_FRAME_NULL) &&
04098                          can_write(chan, confflags)) {
04099                         struct ast_frame *cur;
04100                         /* the translator may have returned a list of frames, so
04101                            write each one onto the channel
04102                         */
04103                         for (cur = conf->transframe[idx]; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
04104                            if (ast_write(chan, cur)) {
04105                               ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", ast_channel_name(chan));
04106                               break;
04107                            }
04108                         }
04109                         if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
04110                            mohtempstopped = 0;
04111                            conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
04112                         }
04113                      }
04114                   } else {
04115                      ast_mutex_unlock(&conf->listenlock);
04116                      goto bailoutandtrynormal;
04117                   }
04118                   ast_mutex_unlock(&conf->listenlock);
04119                } else {
04120 bailoutandtrynormal:
04121                   if (musiconhold && !ast_dsp_silence(dsp, &fr, &confsilence) && confsilence < MEETME_DELAYDETECTTALK) {
04122                      ast_moh_stop(chan);
04123                      mohtempstopped = 1;
04124                   }
04125                   if (user->listen.actual) {
04126                      ast_frame_adjust_volume(&fr, user->listen.actual);
04127                   }
04128                   if (can_write(chan, confflags) && ast_write(chan, &fr) < 0) {
04129                      ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", ast_channel_name(chan));
04130                   }
04131                   if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
04132                      mohtempstopped = 0;
04133                      conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
04134                   }
04135                }
04136             } else {
04137                ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
04138             }
04139          }
04140          lastmarked = currentmarked;
04141       }
04142    }
04143 
04144    if (musiconhold) {
04145       ast_moh_stop(chan);
04146    }
04147    
04148    if (using_pseudo) {
04149       close(fd);
04150    } else {
04151       /* Take out of conference */
04152       dahdic.chan = 0;  
04153       dahdic.confno = 0;
04154       dahdic.confmode = 0;
04155       if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
04156          ast_log(LOG_WARNING, "Error setting conference\n");
04157       }
04158    }
04159 
04160    reset_volumes(user);
04161 
04162    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
04163       !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
04164       conf_play(chan, conf, LEAVE);
04165    }
04166 
04167    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_INTROUSER |CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC) && conf->users > 1) {
04168       struct announce_listitem *item;
04169       if (!(item = ao2_alloc(sizeof(*item), NULL)))
04170          goto outrun;
04171       ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
04172       ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
04173       item->confchan = conf->chan;
04174       item->confusers = conf->users;
04175       item->announcetype = CONF_HASLEFT;
04176       if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
04177          item->vmrec = 1;
04178       }
04179       ast_mutex_lock(&conf->announcelistlock);
04180       AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
04181       ast_cond_signal(&conf->announcelist_addition);
04182       ast_mutex_unlock(&conf->announcelistlock);
04183    } 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) {
04184       /* Last person is leaving, so no reason to try and announce, but should delete the name recording */
04185       ast_filedelete(user->namerecloc, NULL);
04186    }
04187 
04188  outrun:
04189    AST_LIST_LOCK(&confs);
04190 
04191    if (dsp) {
04192       ast_dsp_free(dsp);
04193    }
04194    
04195    if (user->user_no) {
04196       /* Only cleanup users who really joined! */
04197       now = ast_tvnow();
04198 
04199       if (sent_event) {
04200          /*** DOCUMENTATION
04201          <managerEventInstance>
04202             <synopsis>Raised when a user leaves a MeetMe conference.</synopsis>
04203             <syntax>
04204                <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
04205                <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Usernum'])" />
04206                <parameter name="Duration">
04207                   <para>The length of time in seconds that the Meetme user was in the conference.</para>
04208                </parameter>
04209             </syntax>
04210             <see-also>
04211                <ref type="managerEvent">MeetmeJoin</ref>
04212             </see-also>
04213          </managerEventInstance>
04214          ***/
04215          ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeLeave",
04216             "Channel: %s\r\n"
04217             "Uniqueid: %s\r\n"
04218             "Meetme: %s\r\n"
04219             "Usernum: %d\r\n"
04220             "CallerIDNum: %s\r\n"
04221             "CallerIDName: %s\r\n"
04222             "ConnectedLineNum: %s\r\n"
04223             "ConnectedLineName: %s\r\n"
04224             "Duration: %ld\r\n",
04225             ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno,
04226             user->user_no,
04227             S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
04228             S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<unknown>"),
04229             S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, "<unknown>"),
04230             S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "<unknown>"),
04231             (long)(now.tv_sec - user->jointime));
04232       }
04233 
04234       if (setusercount) {
04235          conf->users--;
04236          if (rt_log_members) {
04237             /* Update table */
04238             snprintf(members, sizeof(members), "%d", conf->users);
04239             ast_realtime_require_field("meetme",
04240                "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
04241                "members", RQ_UINTEGER1, strlen(members),
04242                NULL);
04243             ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
04244          }
04245          if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
04246             conf->markedusers--;
04247          }
04248       }
04249       /* Remove ourselves from the container */
04250       ao2_unlink(conf->usercontainer, user); 
04251 
04252       /* Change any states */
04253       if (!conf->users) {
04254          ast_devstate_changed(AST_DEVICE_NOT_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
04255       }
04256 
04257       /* This flag is meant to kill a conference with only one participant remaining.  */
04258       if (conf->users == 1 && ast_test_flag64(confflags, CONFFLAG_KILL_LAST_MAN_STANDING)) {
04259          ao2_callback(conf->usercontainer, 0, user_set_hangup_cb, NULL);
04260       }
04261 
04262       /* Return the number of seconds the user was in the conf */
04263       snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
04264       pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
04265 
04266       /* Return the RealTime bookid for CDR linking */
04267       if (rt_schedule) {
04268          pbx_builtin_setvar_helper(chan, "MEETMEBOOKID", conf->bookid);
04269       }
04270    }
04271    ao2_ref(user, -1);
04272    AST_LIST_UNLOCK(&confs);
04273 
04274 
04275 conf_run_cleanup:
04276    cap_slin = ast_format_cap_destroy(cap_slin);
04277 
04278    return ret;
04279 }
04280 
04281 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
04282             char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags, int *too_early, char **optargs)
04283 {
04284    struct ast_variable *var, *origvar;
04285    struct ast_conference *cnf;
04286 
04287    *too_early = 0;
04288 
04289    /* Check first in the conference list */
04290    AST_LIST_LOCK(&confs);
04291    AST_LIST_TRAVERSE(&confs, cnf, list) {
04292       if (!strcmp(confno, cnf->confno)) {
04293          break;
04294       }
04295    }
04296    if (cnf) {
04297       cnf->refcount += refcount;
04298    }
04299    AST_LIST_UNLOCK(&confs);
04300 
04301    if (!cnf) {
04302       char *pin = NULL, *pinadmin = NULL; /* For temp use */
04303       int maxusers = 0;
04304       struct timeval now;
04305       char recordingfilename[256] = "";
04306       char recordingformat[11] = "";
04307       char currenttime[32] = "";
04308       char eatime[32] = "";
04309       char bookid[51] = "";
04310       char recordingtmp[AST_MAX_EXTENSION] = "";
04311       char useropts[OPTIONS_LEN + 1] = ""; /* Used for RealTime conferences */
04312       char adminopts[OPTIONS_LEN + 1] = "";
04313       struct ast_tm tm, etm;
04314       struct timeval endtime = { .tv_sec = 0 };
04315       const char *var2;
04316 
04317       if (rt_schedule) {
04318          now = ast_tvnow();
04319 
04320          ast_localtime(&now, &tm, NULL);
04321          ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
04322 
04323          ast_debug(1, "Looking for conference %s that starts after %s\n", confno, currenttime);
04324 
04325          var = ast_load_realtime("meetme", "confno",
04326             confno, "starttime <= ", currenttime, "endtime >= ",
04327             currenttime, NULL);
04328 
04329          if (!var && fuzzystart) {
04330             now = ast_tvnow();
04331             now.tv_sec += fuzzystart;
04332 
04333             ast_localtime(&now, &tm, NULL);
04334             ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
04335             var = ast_load_realtime("meetme", "confno",
04336                confno, "starttime <= ", currenttime, "endtime >= ",
04337                currenttime, NULL);
04338          }
04339 
04340          if (!var && earlyalert) {
04341             now = ast_tvnow();
04342             now.tv_sec += earlyalert;
04343             ast_localtime(&now, &etm, NULL);
04344             ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
04345             var = ast_load_realtime("meetme", "confno",
04346                confno, "starttime <= ", eatime, "endtime >= ",
04347                currenttime, NULL);
04348             if (var) {
04349                *too_early = 1;
04350             }
04351          }
04352 
04353       } else {
04354           var = ast_load_realtime("meetme", "confno", confno, NULL);
04355       }
04356 
04357       if (!var) {
04358          return NULL;
04359       }
04360 
04361       if (rt_schedule && *too_early) {
04362          /* Announce that the caller is early and exit */
04363          if (!ast_streamfile(chan, "conf-has-not-started", ast_channel_language(chan))) {
04364             ast_waitstream(chan, "");
04365          }
04366          ast_variables_destroy(var);
04367          return NULL;
04368       }
04369 
04370       for (origvar = var; var; var = var->next) {
04371          if (!strcasecmp(var->name, "pin")) {
04372             pin = ast_strdupa(var->value);
04373          } else if (!strcasecmp(var->name, "adminpin")) {
04374             pinadmin = ast_strdupa(var->value);
04375          } else if (!strcasecmp(var->name, "bookId")) {
04376             ast_copy_string(bookid, var->value, sizeof(bookid));
04377          } else if (!strcasecmp(var->name, "opts")) {
04378             ast_copy_string(useropts, var->value, sizeof(char[OPTIONS_LEN + 1]));
04379          } else if (!strcasecmp(var->name, "maxusers")) {
04380             maxusers = atoi(var->value);
04381          } else if (!strcasecmp(var->name, "adminopts")) {
04382             ast_copy_string(adminopts, var->value, sizeof(char[OPTIONS_LEN + 1]));
04383          } else if (!strcasecmp(var->name, "recordingfilename")) {
04384             ast_copy_string(recordingfilename, var->value, sizeof(recordingfilename));
04385          } else if (!strcasecmp(var->name, "recordingformat")) {
04386             ast_copy_string(recordingformat, var->value, sizeof(recordingformat));
04387          } else if (!strcasecmp(var->name, "endtime")) {
04388             struct ast_tm endtime_tm;
04389             ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
04390             endtime = ast_mktime(&endtime_tm, NULL);
04391          }
04392       }
04393 
04394       ast_variables_destroy(origvar);
04395 
04396       cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan, NULL);
04397 
04398       if (cnf) {
04399          struct ast_flags64 tmp_flags;
04400 
04401          cnf->maxusers = maxusers;
04402          cnf->endalert = endalert;
04403          cnf->endtime = endtime.tv_sec;
04404          cnf->useropts = ast_strdup(useropts);
04405          cnf->adminopts = ast_strdup(adminopts);
04406          cnf->bookid = ast_strdup(bookid);
04407          if (!ast_strlen_zero(recordingfilename)) {
04408             cnf->recordingfilename = ast_strdup(recordingfilename);
04409          }
04410          if (!ast_strlen_zero(recordingformat)) {
04411             cnf->recordingformat = ast_strdup(recordingformat);
04412          }
04413 
04414          /* Parse the other options into confflags -- need to do this in two
04415           * steps, because the parse_options routine zeroes the buffer. */
04416          ast_app_parse_options64(meetme_opts, &tmp_flags, optargs, useropts);
04417          ast_copy_flags64(confflags, &tmp_flags, tmp_flags.flags);
04418 
04419          if (strchr(cnf->useropts, 'r')) {
04420             if (ast_strlen_zero(recordingfilename)) { /* If the recordingfilename in the database is empty, use the channel definition or use the default. */
04421                ast_channel_lock(chan);
04422                if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
04423                   ast_free(cnf->recordingfilename);
04424                   cnf->recordingfilename = ast_strdup(var2);
04425                }
04426                ast_channel_unlock(chan);
04427                if (ast_strlen_zero(cnf->recordingfilename)) {
04428                   snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", cnf->confno, ast_channel_uniqueid(chan));
04429                   ast_free(cnf->recordingfilename);
04430                   cnf->recordingfilename = ast_strdup(recordingtmp);
04431                }
04432             }
04433             if (ast_strlen_zero(cnf->recordingformat)) {/* If the recording format is empty, use the wav as default */
04434                ast_channel_lock(chan);
04435                if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
04436                   ast_free(cnf->recordingformat);
04437                   cnf->recordingformat = ast_strdup(var2);
04438                }
04439                ast_channel_unlock(chan);
04440                if (ast_strlen_zero(cnf->recordingformat)) {
04441                   ast_free(cnf->recordingformat);
04442                   cnf->recordingformat = ast_strdup("wav");
04443                }
04444             }
04445             ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
04446          }
04447       }
04448    }
04449 
04450    if (cnf) {
04451       if (confflags->flags && !cnf->chan &&
04452           !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
04453           ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW) | CONFFLAG_INTROUSER_VMREC) {
04454          ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
04455          ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC);
04456       }
04457 
04458       if (confflags && !cnf->chan &&
04459           ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
04460          ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
04461          ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
04462       }
04463    }
04464 
04465    return cnf;
04466 }
04467 
04468 
04469 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
04470                char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags)
04471 {
04472    struct ast_config *cfg;
04473    struct ast_variable *var;
04474    struct ast_flags config_flags = { 0 };
04475    struct ast_conference *cnf;
04476 
04477    AST_DECLARE_APP_ARGS(args,
04478       AST_APP_ARG(confno);
04479       AST_APP_ARG(pin);
04480       AST_APP_ARG(pinadmin);
04481    );
04482 
04483    /* Check first in the conference list */
04484    ast_debug(1, "The requested confno is '%s'?\n", confno);
04485    AST_LIST_LOCK(&confs);
04486    AST_LIST_TRAVERSE(&confs, cnf, list) {
04487       ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
04488       if (!strcmp(confno, cnf->confno))
04489          break;
04490    }
04491    if (cnf) {
04492       cnf->refcount += refcount;
04493    }
04494    AST_LIST_UNLOCK(&confs);
04495 
04496    if (!cnf) {
04497       if (dynamic) {
04498          /* No need to parse meetme.conf */
04499          ast_debug(1, "Building dynamic conference '%s'\n", confno);
04500          if (dynamic_pin) {
04501             if (dynamic_pin[0] == 'q') {
04502                /* Query the user to enter a PIN */
04503                if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
04504                   return NULL;
04505             }
04506             cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan, NULL);
04507          } else {
04508             cnf = build_conf(confno, "", "", make, dynamic, refcount, chan, NULL);
04509          }
04510       } else {
04511          /* Check the config */
04512          cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04513          if (!cfg) {
04514             ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
04515             return NULL;
04516          } else if (cfg == CONFIG_STATUS_FILEINVALID) {
04517             ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format.  Aborting.\n");
04518             return NULL;
04519          }
04520 
04521          for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
04522             char parse[MAX_SETTINGS];
04523 
04524             if (strcasecmp(var->name, "conf"))
04525                continue;
04526 
04527             ast_copy_string(parse, var->value, sizeof(parse));
04528 
04529             AST_STANDARD_APP_ARGS(args, parse);
04530             ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
04531             if (!strcasecmp(args.confno, confno)) {
04532                /* Bingo it's a valid conference */
04533                cnf = build_conf(args.confno,
04534                      S_OR(args.pin, ""),
04535                      S_OR(args.pinadmin, ""),
04536                      make, dynamic, refcount, chan, NULL);
04537                break;
04538             }
04539          }
04540          if (!var) {
04541             ast_debug(1, "%s isn't a valid conference\n", confno);
04542          }
04543          ast_config_destroy(cfg);
04544       }
04545    } else if (dynamic_pin) {
04546       /* Correct for the user selecting 'D' instead of 'd' to have
04547          someone join into a conference that has already been created
04548          with a pin. */
04549       if (dynamic_pin[0] == 'q') {
04550          dynamic_pin[0] = '\0';
04551       }
04552    }
04553 
04554    if (cnf) {
04555       if (confflags && !cnf->chan &&
04556           !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
04557           ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW  | CONFFLAG_INTROUSER_VMREC)) {
04558          ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
04559          ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC);
04560       }
04561       
04562       if (confflags && !cnf->chan &&
04563           ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
04564          ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
04565          ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
04566       }
04567    }
04568 
04569    return cnf;
04570 }
04571 
04572 /*! \brief The MeetmeCount application */
04573 static int count_exec(struct ast_channel *chan, const char *data)
04574 {
04575    int res = 0;
04576    struct ast_conference *conf;
04577    int count;
04578    char *localdata;
04579    char val[80] = "0"; 
04580    AST_DECLARE_APP_ARGS(args,
04581       AST_APP_ARG(confno);
04582       AST_APP_ARG(varname);
04583    );
04584 
04585    if (ast_strlen_zero(data)) {
04586       ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
04587       return -1;
04588    }
04589    
04590    localdata = ast_strdupa(data);
04591 
04592    AST_STANDARD_APP_ARGS(args, localdata);
04593    
04594    conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
04595 
04596    if (conf) {
04597       count = conf->users;
04598       dispose_conf(conf);
04599       conf = NULL;
04600    } else
04601       count = 0;
04602 
04603    if (!ast_strlen_zero(args.varname)) {
04604       /* have var so load it and exit */
04605       snprintf(val, sizeof(val), "%d", count);
04606       pbx_builtin_setvar_helper(chan, args.varname, val);
04607    } else {
04608       if (ast_channel_state(chan) != AST_STATE_UP) {
04609          ast_answer(chan);
04610       }
04611       res = ast_say_number(chan, count, "", ast_channel_language(chan), (char *) NULL); /* Needs gender */
04612    }
04613 
04614    return res;
04615 }
04616 
04617 /*! \brief The meetme() application */
04618 static int conf_exec(struct ast_channel *chan, const char *data)
04619 {
04620    int res = -1;
04621    char confno[MAX_CONFNUM] = "";
04622    int allowretry = 0;
04623    int retrycnt = 0;
04624    struct ast_conference *cnf = NULL;
04625    struct ast_flags64 confflags = {0};
04626    struct ast_flags config_flags = { 0 };
04627    int dynamic = 0;
04628    int empty = 0, empty_no_pin = 0;
04629    int always_prompt = 0;
04630    const char *notdata;
04631    char *info, the_pin[MAX_PIN] = "";
04632    AST_DECLARE_APP_ARGS(args,
04633       AST_APP_ARG(confno);
04634       AST_APP_ARG(options);
04635       AST_APP_ARG(pin);
04636    );
04637    char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
04638 
04639    if (ast_strlen_zero(data)) {
04640       allowretry = 1;
04641       notdata = "";
04642    } else {
04643       notdata = data;
04644    }
04645    
04646    if (ast_channel_state(chan) != AST_STATE_UP)
04647       ast_answer(chan);
04648 
04649    info = ast_strdupa(notdata);
04650 
04651    AST_STANDARD_APP_ARGS(args, info);  
04652 
04653    if (args.confno) {
04654       ast_copy_string(confno, args.confno, sizeof(confno));
04655       if (ast_strlen_zero(confno)) {
04656          allowretry = 1;
04657       }
04658    }
04659    
04660    if (args.pin)
04661       ast_copy_string(the_pin, args.pin, sizeof(the_pin));
04662 
04663    if (args.options) {
04664       ast_app_parse_options64(meetme_opts, &confflags, optargs, args.options);
04665       dynamic = ast_test_flag64(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
04666       if (ast_test_flag64(&confflags, CONFFLAG_DYNAMICPIN) && ast_strlen_zero(args.pin))
04667          strcpy(the_pin, "q");
04668 
04669       empty = ast_test_flag64(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
04670       empty_no_pin = ast_test_flag64(&confflags, CONFFLAG_EMPTYNOPIN);
04671       always_prompt = ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT | CONFFLAG_DYNAMICPIN);
04672    }
04673 
04674    do {
04675       if (retrycnt > 3)
04676          allowretry = 0;
04677       if (empty) {
04678          int i;
04679          struct ast_config *cfg;
04680          struct ast_variable *var;
04681          int confno_int;
04682 
04683          /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
04684          if ((empty_no_pin) || (!dynamic)) {
04685             cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04686             if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
04687                var = ast_variable_browse(cfg, "rooms");
04688                while (var) {
04689                   char parse[MAX_SETTINGS], *stringp = parse, *confno_tmp;
04690                   if (!strcasecmp(var->name, "conf")) {
04691                      int found = 0;
04692                      ast_copy_string(parse, var->value, sizeof(parse));
04693                      confno_tmp = strsep(&stringp, "|,");
04694                      if (!dynamic) {
04695                         /* For static:  run through the list and see if this conference is empty */
04696                         AST_LIST_LOCK(&confs);
04697                         AST_LIST_TRAVERSE(&confs, cnf, list) {
04698                            if (!strcmp(confno_tmp, cnf->confno)) {
04699                               /* The conference exists, therefore it's not empty */
04700                               found = 1;
04701                               break;
04702                            }
04703                         }
04704                         AST_LIST_UNLOCK(&confs);
04705                         cnf = NULL;
04706                         if (!found) {
04707                            /* At this point, we have a confno_tmp (static conference) that is empty */
04708                            if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
04709                               /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
04710                                * Case 2:  empty_no_pin and pin is blank (but not NULL)
04711                                * Case 3:  not empty_no_pin
04712                                */
04713                               ast_copy_string(confno, confno_tmp, sizeof(confno));
04714                               break;
04715                            }
04716                         }
04717                      }
04718                   }
04719                   var = var->next;
04720                }
04721                ast_config_destroy(cfg);
04722             }
04723 
04724             if (ast_strlen_zero(confno) && (cfg = ast_load_realtime_multientry("meetme", "confno LIKE", "%", SENTINEL))) {
04725                const char *catg;
04726                for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
04727                   const char *confno_tmp = ast_variable_retrieve(cfg, catg, "confno");
04728                   const char *pin_tmp = ast_variable_retrieve(cfg, catg, "pin");
04729                   if (ast_strlen_zero(confno_tmp)) {
04730                      continue;
04731                   }
04732                   if (!dynamic) {
04733                      int found = 0;
04734                      /* For static:  run through the list and see if this conference is empty */
04735                      AST_LIST_LOCK(&confs);
04736                      AST_LIST_TRAVERSE(&confs, cnf, list) {
04737                         if (!strcmp(confno_tmp, cnf->confno)) {
04738                            /* The conference exists, therefore it's not empty */
04739                            found = 1;
04740                            break;
04741                         }
04742                      }
04743                      AST_LIST_UNLOCK(&confs);
04744                      if (!found) {
04745                         /* At this point, we have a confno_tmp (realtime conference) that is empty */
04746                         if ((empty_no_pin && ast_strlen_zero(pin_tmp)) || (!empty_no_pin)) {
04747                            /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
04748                             * Case 2:  empty_no_pin and pin is blank (but not NULL)
04749                             * Case 3:  not empty_no_pin
04750                             */
04751                            ast_copy_string(confno, confno_tmp, sizeof(confno));
04752                            break;
04753                         }
04754                      }
04755                   }
04756                }
04757                ast_config_destroy(cfg);
04758             }
04759          }
04760 
04761          /* Select first conference number not in use */
04762          if (ast_strlen_zero(confno) && dynamic) {
04763             AST_LIST_LOCK(&confs);
04764             for (i = 0; i < ARRAY_LEN(conf_map); i++) {
04765                if (!conf_map[i]) {
04766                   snprintf(confno, sizeof(confno), "%d", i);
04767                   conf_map[i] = 1;
04768                   break;
04769                }
04770             }
04771             AST_LIST_UNLOCK(&confs);
04772          }
04773 
04774          /* Not found? */
04775          if (ast_strlen_zero(confno)) {
04776             res = ast_streamfile(chan, "conf-noempty", ast_channel_language(chan));
04777             ast_test_suite_event_notify("PLAYBACK", "Message: conf-noempty");
04778             if (!res)
04779                ast_waitstream(chan, "");
04780          } else {
04781             if (sscanf(confno, "%30d", &confno_int) == 1) {
04782                if (!ast_test_flag64(&confflags, CONFFLAG_QUIET)) {
04783                   res = ast_streamfile(chan, "conf-enteringno", ast_channel_language(chan));
04784                   if (!res) {
04785                      ast_waitstream(chan, "");
04786                      res = ast_say_digits(chan, confno_int, "", ast_channel_language(chan));
04787                   }
04788                }
04789             } else {
04790                ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
04791             }
04792          }
04793       }
04794 
04795       while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
04796          /* Prompt user for conference number */
04797          res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
04798          if (res < 0) {
04799             /* Don't try to validate when we catch an error */
04800             confno[0] = '\0';
04801             allowretry = 0;
04802             break;
04803          }
04804       }
04805       if (!ast_strlen_zero(confno)) {
04806          /* Check the validity of the conference */
04807          cnf = find_conf(chan, confno, 1, dynamic, the_pin, 
04808             sizeof(the_pin), 1, &confflags);
04809          if (!cnf) {
04810             int too_early = 0;
04811 
04812             cnf = find_conf_realtime(chan, confno, 1, dynamic, 
04813                the_pin, sizeof(the_pin), 1, &confflags, &too_early, optargs);
04814             if (rt_schedule && too_early)
04815                allowretry = 0;
04816          }
04817 
04818          if (!cnf) {
04819             if (allowretry) {
04820                confno[0] = '\0';
04821                res = ast_streamfile(chan, "conf-invalid", ast_channel_language(chan));
04822                if (!res)
04823                   ast_waitstream(chan, "");
04824                res = -1;
04825             }
04826          } else {
04827             /* Conference requires a pin for specified access level */
04828             int req_pin = !ast_strlen_zero(cnf->pin) ||
04829                (!ast_strlen_zero(cnf->pinadmin) &&
04830                   ast_test_flag64(&confflags, CONFFLAG_ADMIN));
04831             /* The following logic was derived from a
04832              * 4 variable truth table and defines which
04833              * circumstances are not exempt from pin
04834              * checking.
04835              * If this needs to be modified, write the
04836              * truth table back out from the boolean
04837              * expression AB+A'D+C', change the erroneous
04838              * result, and rederive the expression.
04839              * Variables:
04840              *  A: pin provided?
04841              *  B: always prompt?
04842              *  C: dynamic?
04843              *  D: has users? */
04844             int not_exempt = !cnf->isdynamic;
04845             not_exempt = not_exempt || (!ast_strlen_zero(args.pin) && ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT));
04846             not_exempt = not_exempt || (ast_strlen_zero(args.pin) && cnf->users);
04847             if (req_pin && not_exempt) {
04848                char pin[MAX_PIN] = "";
04849                int j;
04850 
04851                /* Allow the pin to be retried up to 3 times */
04852                for (j = 0; j < 3; j++) {
04853                   if (*the_pin && (always_prompt == 0)) {
04854                      ast_copy_string(pin, the_pin, sizeof(pin));
04855                      res = 0;
04856                   } else {
04857                      /* Prompt user for pin if pin is required */
04858                      ast_test_suite_event_notify("PLAYBACK", "Message: conf-getpin\r\n"
04859                         "Channel: %s",
04860                         ast_channel_name(chan));
04861                      res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
04862                   }
04863                   if (res >= 0) {
04864                      if ((!strcasecmp(pin, cnf->pin) &&
04865                           (ast_strlen_zero(cnf->pinadmin) ||
04866                            !ast_test_flag64(&confflags, CONFFLAG_ADMIN))) ||
04867                           (!ast_strlen_zero(cnf->pinadmin) &&
04868                            !strcasecmp(pin, cnf->pinadmin))) {
04869                         /* Pin correct */
04870                         allowretry = 0;
04871                         if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) {
04872                            if (!ast_strlen_zero(cnf->adminopts)) {
04873                               char *opts = ast_strdupa(cnf->adminopts);
04874                               ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04875                            }
04876                         } else {
04877                            if (!ast_strlen_zero(cnf->useropts)) {
04878                               char *opts = ast_strdupa(cnf->useropts);
04879                               ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04880                            }
04881                         }
04882                         /* Run the conference */
04883                         ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
04884                         res = conf_run(chan, cnf, &confflags, optargs);
04885                         break;
04886                      } else {
04887                         /* Pin invalid */
04888                         if (!ast_streamfile(chan, "conf-invalidpin", ast_channel_language(chan))) {
04889                            res = ast_waitstream(chan, AST_DIGIT_ANY);
04890                            ast_stopstream(chan);
04891                         } else {
04892                            ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
04893                            break;
04894                         }
04895                         if (res < 0)
04896                            break;
04897                         pin[0] = res;
04898                         pin[1] = '\0';
04899                         res = -1;
04900                         if (allowretry)
04901                            confno[0] = '\0';
04902                      }
04903                   } else {
04904                      /* failed when getting the pin */
04905                      res = -1;
04906                      allowretry = 0;
04907                      /* see if we need to get rid of the conference */
04908                      break;
04909                   }
04910 
04911                   /* Don't retry pin with a static pin */
04912                   if (*the_pin && (always_prompt == 0)) {
04913                      break;
04914                   }
04915                }
04916             } else {
04917                /* No pin required */
04918                allowretry = 0;
04919 
04920                /* For RealTime conferences without a pin 
04921                 * should still support loading options
04922                 */
04923                if (!ast_strlen_zero(cnf->useropts)) {
04924                   char *opts = ast_strdupa(cnf->useropts);
04925                   ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04926                }
04927 
04928                /* Run the conference */
04929                res = conf_run(chan, cnf, &confflags, optargs);
04930             }
04931             dispose_conf(cnf);
04932             cnf = NULL;
04933          }
04934       }
04935    } while (allowretry);
04936 
04937    if (cnf)
04938       dispose_conf(cnf);
04939    
04940    return res;
04941 }
04942 
04943 static struct ast_conf_user *find_user(struct ast_conference *conf, const char *callerident)
04944 {
04945    struct ast_conf_user *user = NULL;
04946    int cid;
04947 
04948    if (conf && callerident && sscanf(callerident, "%30d", &cid) == 1) {
04949       user = ao2_find(conf->usercontainer, &cid, 0);
04950       /* reference decremented later in admin_exec */
04951       return user;
04952    }
04953    return NULL;
04954 }
04955 
04956 static int user_listen_volup_cb(void *obj, void *unused, int flags)
04957 {
04958    struct ast_conf_user *user = obj;
04959    tweak_listen_volume(user, VOL_UP);
04960    return 0;
04961 }
04962 
04963 static int user_listen_voldown_cb(void *obj, void *unused, int flags)
04964 {
04965    struct ast_conf_user *user = obj;
04966    tweak_listen_volume(user, VOL_DOWN);
04967    return 0;
04968 }
04969 
04970 static int user_talk_volup_cb(void *obj, void *unused, int flags)
04971 {
04972    struct ast_conf_user *user = obj;
04973    tweak_talk_volume(user, VOL_UP);
04974    return 0;
04975 }
04976 
04977 static int user_talk_voldown_cb(void *obj, void *unused, int flags)
04978 {
04979    struct ast_conf_user *user = obj;
04980    tweak_talk_volume(user, VOL_DOWN);
04981    return 0;
04982 }
04983 
04984 static int user_reset_vol_cb(void *obj, void *unused, int flags)
04985 {
04986    struct ast_conf_user *user = obj;
04987    reset_volumes(user);
04988    return 0;
04989 }
04990 
04991 static int user_chan_cb(void *obj, void *args, int flags)
04992 {
04993    struct ast_conf_user *user = obj;
04994    const char *channel = args;
04995 
04996    if (!strcmp(ast_channel_name(user->chan), channel)) {
04997       return (CMP_MATCH | CMP_STOP);
04998    }
04999 
05000    return 0;
05001 }
05002 
05003 /*! \brief The MeetMeadmin application 
05004 
05005   MeetMeAdmin(confno, command, caller) */
05006 static int admin_exec(struct ast_channel *chan, const char *data) {
05007    char *params;
05008    struct ast_conference *cnf;
05009    struct ast_conf_user *user = NULL;
05010    AST_DECLARE_APP_ARGS(args,
05011       AST_APP_ARG(confno);
05012       AST_APP_ARG(command);
05013       AST_APP_ARG(user);
05014    );
05015    int res = 0;
05016 
05017    if (ast_strlen_zero(data)) {
05018       ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
05019       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
05020       return -1;
05021    }
05022 
05023    params = ast_strdupa(data);
05024    AST_STANDARD_APP_ARGS(args, params);
05025 
05026    if (!args.command) {
05027       ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
05028       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
05029       return -1;
05030    }
05031 
05032    AST_LIST_LOCK(&confs);
05033    AST_LIST_TRAVERSE(&confs, cnf, list) {
05034       if (!strcmp(cnf->confno, args.confno))
05035          break;
05036    }
05037 
05038    if (!cnf) {
05039       ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
05040       AST_LIST_UNLOCK(&confs);
05041       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOTFOUND");
05042       return 0;
05043    }
05044 
05045    ast_atomic_fetchadd_int(&cnf->refcount, 1);
05046 
05047    if (args.user) {
05048       user = find_user(cnf, args.user);
05049       if (!user) {
05050          ast_log(LOG_NOTICE, "Specified User not found!\n");
05051          res = -2;
05052          goto usernotfound;
05053       }
05054    } else {
05055       /* fail for commands that require a user */
05056       switch (*args.command) {
05057       case 'm': /* Unmute */
05058       case 'M': /* Mute */
05059       case 't': /* Lower user's talk volume */
05060       case 'T': /* Raise user's talk volume */
05061       case 'u': /* Lower user's listen volume */
05062       case 'U': /* Raise user's listen volume */
05063       case 'r': /* Reset user's volume level */
05064       case 'k': /* Kick user */
05065          res = -2;
05066          ast_log(LOG_NOTICE, "No user specified!\n");
05067          goto usernotfound;
05068       default:
05069          break;
05070       }
05071    }
05072 
05073    switch (*args.command) {
05074    case 76: /* L: Lock */ 
05075       cnf->locked = 1;
05076       break;
05077    case 108: /* l: Unlock */ 
05078       cnf->locked = 0;
05079       break;
05080    case 75: /* K: kick all users */
05081       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_kickme_cb, NULL);
05082       break;
05083    case 101: /* e: Eject last user*/
05084    {
05085       int max_no = 0;
05086       RAII_VAR(struct ast_conf_user *, eject_user, NULL, ao2_cleanup);
05087 
05088       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
05089       eject_user = ao2_find(cnf->usercontainer, &max_no, 0);
05090       if (!eject_user) {
05091          res = -1;
05092          ast_log(LOG_NOTICE, "No last user to kick!\n");
05093          break;
05094       }
05095 
05096       if (!ast_test_flag64(&eject_user->userflags, CONFFLAG_ADMIN)) {
05097          eject_user->adminflags |= ADMINFLAG_KICKME;
05098       } else {
05099          res = -1;
05100          ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
05101       }
05102       break;
05103    }
05104    case 77: /* M: Mute */ 
05105       user->adminflags |= ADMINFLAG_MUTED;
05106       break;
05107    case 78: /* N: Mute all (non-admin) users */
05108       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_muted_cb, &cnf);
05109       break;               
05110    case 109: /* m: Unmute */ 
05111       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
05112       break;
05113    case 110: /* n: Unmute all users */
05114       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, NULL);
05115       break;
05116    case 107: /* k: Kick user */ 
05117       user->adminflags |= ADMINFLAG_KICKME;
05118       break;
05119    case 118: /* v: Lower all users listen volume */
05120       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_voldown_cb, NULL);
05121       break;
05122    case 86: /* V: Raise all users listen volume */
05123       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_volup_cb, NULL);
05124       break;
05125    case 115: /* s: Lower all users speaking volume */
05126       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_voldown_cb, NULL);
05127       break;
05128    case 83: /* S: Raise all users speaking volume */
05129       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_volup_cb, NULL);
05130       break;
05131    case 82: /* R: Reset all volume levels */
05132       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_reset_vol_cb, NULL);
05133       break;
05134    case 114: /* r: Reset user's volume level */
05135       reset_volumes(user);
05136       break;
05137    case 85: /* U: Raise user's listen volume */
05138       tweak_listen_volume(user, VOL_UP);
05139       break;
05140    case 117: /* u: Lower user's listen volume */
05141       tweak_listen_volume(user, VOL_DOWN);
05142       break;
05143    case 84: /* T: Raise user's talk volume */
05144       tweak_talk_volume(user, VOL_UP);
05145       break;
05146    case 116: /* t: Lower user's talk volume */
05147       tweak_talk_volume(user, VOL_DOWN);
05148       break;
05149    case 'E': /* E: Extend conference */
05150       if (rt_extend_conf(args.confno)) {
05151          res = -1;
05152       }
05153       break;
05154    }
05155 
05156    if (args.user) {
05157       /* decrement reference from find_user */
05158       ao2_ref(user, -1);
05159    }
05160 usernotfound:
05161    AST_LIST_UNLOCK(&confs);
05162 
05163    dispose_conf(cnf);
05164    pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", res == -2 ? "NOTFOUND" : res ? "FAILED" : "OK");
05165 
05166    return 0;
05167 }
05168 
05169 /*! \brief The MeetMeChannelAdmin application 
05170    MeetMeChannelAdmin(channel, command) */
05171 static int channel_admin_exec(struct ast_channel *chan, const char *data) {
05172    char *params;
05173    struct ast_conference *conf = NULL;
05174    struct ast_conf_user *user = NULL;
05175    AST_DECLARE_APP_ARGS(args,
05176       AST_APP_ARG(channel);
05177       AST_APP_ARG(command);
05178    );
05179 
05180    if (ast_strlen_zero(data)) {
05181       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
05182       return -1;
05183    }
05184    
05185    params = ast_strdupa(data);
05186    AST_STANDARD_APP_ARGS(args, params);
05187 
05188    if (!args.channel) {
05189       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
05190       return -1;
05191    }
05192 
05193    if (!args.command) {
05194       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
05195       return -1;
05196    }
05197 
05198    AST_LIST_LOCK(&confs);
05199    AST_LIST_TRAVERSE(&confs, conf, list) {
05200       if ((user = ao2_callback(conf->usercontainer, 0, user_chan_cb, args.channel))) {
05201          break;
05202       }
05203    }
05204    
05205    if (!user) {
05206       ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
05207       AST_LIST_UNLOCK(&confs);
05208       return 0;
05209    }
05210    
05211    /* perform the specified action */
05212    switch (*args.command) {
05213       case 77: /* M: Mute */ 
05214          user->adminflags |= ADMINFLAG_MUTED;
05215          break;
05216       case 109: /* m: Unmute */ 
05217          user->adminflags &= ~ADMINFLAG_MUTED;
05218          break;
05219       case 107: /* k: Kick user */ 
05220          user->adminflags |= ADMINFLAG_KICKME;
05221          break;
05222       default: /* unknown command */
05223          ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
05224          break;
05225    }
05226    ao2_ref(user, -1);
05227    AST_LIST_UNLOCK(&confs);
05228    
05229    return 0;
05230 }
05231 
05232 static int meetmemute(struct mansession *s, const struct message *m, int mute)
05233 {
05234    struct ast_conference *conf;
05235    struct ast_conf_user *user;
05236    const char *confid = astman_get_header(m, "Meetme");
05237    char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
05238    int userno;
05239 
05240    if (ast_strlen_zero(confid)) {
05241       astman_send_error(s, m, "Meetme conference not specified");
05242       return 0;
05243    }
05244 
05245    if (ast_strlen_zero(userid)) {
05246       astman_send_error(s, m, "Meetme user number not specified");
05247       return 0;
05248    }
05249 
05250    userno = strtoul(userid, &userid, 10);
05251 
05252    if (*userid) {
05253       astman_send_error(s, m, "Invalid user number");
05254       return 0;
05255    }
05256 
05257    /* Look in the conference list */
05258    AST_LIST_LOCK(&confs);
05259    AST_LIST_TRAVERSE(&confs, conf, list) {
05260       if (!strcmp(confid, conf->confno))
05261          break;
05262    }
05263 
05264    if (!conf) {
05265       AST_LIST_UNLOCK(&confs);
05266       astman_send_error(s, m, "Meetme conference does not exist");
05267       return 0;
05268    }
05269 
05270    user = ao2_find(conf->usercontainer, &userno, 0);
05271 
05272    if (!user) {
05273       AST_LIST_UNLOCK(&confs);
05274       astman_send_error(s, m, "User number not found");
05275       return 0;
05276    }
05277 
05278    if (mute)
05279       user->adminflags |= ADMINFLAG_MUTED;   /* request user muting */
05280    else
05281       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST); /* request user unmuting */
05282 
05283    AST_LIST_UNLOCK(&confs);
05284 
05285    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));
05286 
05287    ao2_ref(user, -1);
05288    astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
05289    return 0;
05290 }
05291 
05292 static int action_meetmemute(struct mansession *s, const struct message *m)
05293 {
05294    return meetmemute(s, m, 1);
05295 }
05296 
05297 static int action_meetmeunmute(struct mansession *s, const struct message *m)
05298 {
05299    return meetmemute(s, m, 0);
05300 }
05301 
05302 static int action_meetmelist(struct mansession *s, const struct message *m)
05303 {
05304    const char *actionid = astman_get_header(m, "ActionID");
05305    const char *conference = astman_get_header(m, "Conference");
05306    char idText[80] = "";
05307    struct ast_conference *cnf;
05308    struct ast_conf_user *user;
05309    struct ao2_iterator user_iter;
05310    int total = 0;
05311 
05312    if (!ast_strlen_zero(actionid))
05313       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
05314 
05315    if (AST_LIST_EMPTY(&confs)) {
05316       astman_send_error(s, m, "No active conferences.");
05317       return 0;
05318    }
05319 
05320    astman_send_listack(s, m, "Meetme user list will follow", "start");
05321 
05322    /* Find the right conference */
05323    AST_LIST_LOCK(&confs);
05324    AST_LIST_TRAVERSE(&confs, cnf, list) {
05325       /* If we ask for one particular, and this isn't it, skip it */
05326       if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
05327          continue;
05328 
05329       /* Show all the users */
05330       user_iter = ao2_iterator_init(cnf->usercontainer, 0);
05331       while ((user = ao2_iterator_next(&user_iter))) {
05332          total++;
05333          astman_append(s,
05334             "Event: MeetmeList\r\n"
05335             "%s"
05336             "Conference: %s\r\n"
05337             "UserNumber: %d\r\n"
05338             "CallerIDNum: %s\r\n"
05339             "CallerIDName: %s\r\n"
05340             "ConnectedLineNum: %s\r\n"
05341             "ConnectedLineName: %s\r\n"
05342             "Channel: %s\r\n"
05343             "Admin: %s\r\n"
05344             "Role: %s\r\n"
05345             "MarkedUser: %s\r\n"
05346             "Muted: %s\r\n"
05347             "Talking: %s\r\n"
05348             "\r\n",
05349             idText,
05350             cnf->confno,
05351             user->user_no,
05352             S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
05353             S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
05354             S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, "<unknown>"),
05355             S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "<no name>"),
05356             ast_channel_name(user->chan),
05357             ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "Yes" : "No",
05358             ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "Listen only" : ast_test_flag64(&user->userflags, CONFFLAG_TALKER) ? "Talk only" : "Talk and listen",
05359             ast_test_flag64(&user->userflags, CONFFLAG_MARKEDUSER) ? "Yes" : "No",
05360             user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
05361             user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
05362          ao2_ref(user, -1);
05363       }
05364       ao2_iterator_destroy(&user_iter);
05365    }
05366    AST_LIST_UNLOCK(&confs);
05367    /* Send final confirmation */
05368    astman_append(s,
05369    "Event: MeetmeListComplete\r\n"
05370    "EventList: Complete\r\n"
05371    "ListItems: %d\r\n"
05372    "%s"
05373    "\r\n", total, idText);
05374    return 0;
05375 }
05376 
05377 static int action_meetmelistrooms(struct mansession *s, const struct message *m)
05378 {
05379    const char *actionid = astman_get_header(m, "ActionID");
05380    char idText[80] = "";
05381    struct ast_conference *cnf;
05382    int totalitems = 0;
05383    int hr, min, sec;
05384    time_t now;
05385    char markedusers[5];
05386 
05387    if (!ast_strlen_zero(actionid)) {
05388       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
05389    }
05390 
05391    if (AST_LIST_EMPTY(&confs)) {
05392       astman_send_error(s, m, "No active conferences.");
05393       return 0;
05394    }
05395 
05396    astman_send_listack(s, m, "Meetme conferences will follow", "start");
05397 
05398    now = time(NULL);
05399 
05400    /* Traverse the conference list */
05401    AST_LIST_LOCK(&confs);
05402    AST_LIST_TRAVERSE(&confs, cnf, list) {
05403       totalitems++;
05404 
05405       if (cnf->markedusers == 0) {
05406          strcpy(markedusers, "N/A");
05407       } else {
05408          sprintf(markedusers, "%.4d", cnf->markedusers);
05409       }
05410       hr = (now - cnf->start) / 3600;
05411       min = ((now - cnf->start) % 3600) / 60;
05412       sec = (now - cnf->start) % 60;
05413 
05414       astman_append(s,
05415       "Event: MeetmeListRooms\r\n"
05416       "%s"
05417       "Conference: %s\r\n"
05418       "Parties: %d\r\n"
05419       "Marked: %s\r\n"
05420       "Activity: %2.2d:%2.2d:%2.2d\r\n"
05421       "Creation: %s\r\n"
05422       "Locked: %s\r\n"
05423       "\r\n",
05424       idText,
05425       cnf->confno,
05426       cnf->users,
05427       markedusers,
05428       hr,  min, sec,
05429       cnf->isdynamic ? "Dynamic" : "Static",
05430       cnf->locked ? "Yes" : "No"); 
05431    }
05432    AST_LIST_UNLOCK(&confs);
05433 
05434    /* Send final confirmation */
05435    astman_append(s,
05436    "Event: MeetmeListRoomsComplete\r\n"
05437    "EventList: Complete\r\n"
05438    "ListItems: %d\r\n"
05439    "%s"
05440    "\r\n", totalitems, idText);
05441    return 0;
05442 }
05443 
05444 /*! \internal
05445  * \brief creates directory structure and assigns absolute path from relative paths for filenames
05446  *
05447  * \param filename contains the absolute or relative path to the desired file
05448  * \param buffer stores completed filename, absolutely must be a buffer of PATH_MAX length
05449  */
05450 static void filename_parse(char *filename, char *buffer)
05451 {
05452    char *slash;
05453    if (ast_strlen_zero(filename)) {
05454       ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
05455    } else if (filename[0] != '/') {
05456       snprintf(buffer, PATH_MAX, "%s/meetme/%s", ast_config_AST_SPOOL_DIR, filename);
05457    } else {
05458       ast_copy_string(buffer, filename, PATH_MAX);
05459    }
05460 
05461    slash = buffer;
05462    if ((slash = strrchr(slash, '/'))) {
05463       *slash = '\0';
05464       ast_mkdir(buffer, 0777);
05465       *slash = '/';
05466    }
05467 }
05468 
05469 static void *recordthread(void *args)
05470 {
05471    struct ast_conference *cnf = args;
05472    struct ast_frame *f = NULL;
05473    int flags;
05474    struct ast_filestream *s = NULL;
05475    int res = 0;
05476    int x;
05477    const char *oldrecordingfilename = NULL;
05478    char filename_buffer[PATH_MAX];
05479 
05480    if (!cnf || !cnf->lchan) {
05481       pthread_exit(0);
05482    }
05483 
05484    filename_buffer[0] = '\0';
05485    filename_parse(cnf->recordingfilename, filename_buffer);
05486 
05487    ast_stopstream(cnf->lchan);
05488    flags = O_CREAT | O_TRUNC | O_WRONLY;
05489 
05490 
05491    cnf->recording = MEETME_RECORD_ACTIVE;
05492    while (ast_waitfor(cnf->lchan, -1) > -1) {
05493       if (cnf->recording == MEETME_RECORD_TERMINATE) {
05494          AST_LIST_LOCK(&confs);
05495          AST_LIST_UNLOCK(&confs);
05496          break;
05497       }
05498       if (!s && !(ast_strlen_zero(filename_buffer)) && (filename_buffer != oldrecordingfilename)) {
05499          s = ast_writefile(filename_buffer, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
05500          oldrecordingfilename = filename_buffer;
05501       }
05502       
05503       f = ast_read(cnf->lchan);
05504       if (!f) {
05505          res = -1;
05506          break;
05507       }
05508       if (f->frametype == AST_FRAME_VOICE) {
05509          ast_mutex_lock(&cnf->listenlock);
05510          for (x = 0; x < AST_FRAME_BITS; x++) {
05511             /* Free any translations that have occured */
05512             if (cnf->transframe[x]) {
05513                ast_frfree(cnf->transframe[x]);
05514                cnf->transframe[x] = NULL;
05515             }
05516          }
05517          if (cnf->origframe)
05518             ast_frfree(cnf->origframe);
05519          cnf->origframe = ast_frdup(f);
05520          ast_mutex_unlock(&cnf->listenlock);
05521          if (s)
05522             res = ast_writestream(s, f);
05523          if (res) {
05524             ast_frfree(f);
05525             break;
05526          }
05527       }
05528       ast_frfree(f);
05529    }
05530    cnf->recording = MEETME_RECORD_OFF;
05531    if (s)
05532       ast_closestream(s);
05533    
05534    pthread_exit(0);
05535 }
05536 
05537 /*! \brief Callback for devicestate providers */
05538 static enum ast_device_state meetmestate(const char *data)
05539 {
05540    struct ast_conference *conf;
05541 
05542    /* Find conference */
05543    AST_LIST_LOCK(&confs);
05544    AST_LIST_TRAVERSE(&confs, conf, list) {
05545       if (!strcmp(data, conf->confno))
05546          break;
05547    }
05548    AST_LIST_UNLOCK(&confs);
05549    if (!conf)
05550       return AST_DEVICE_INVALID;
05551 
05552 
05553    /* SKREP to fill */
05554    if (!conf->users)
05555       return AST_DEVICE_NOT_INUSE;
05556 
05557    return AST_DEVICE_INUSE;
05558 }
05559 
05560 static void load_config_meetme(void)
05561 {
05562    struct ast_config *cfg;
05563    struct ast_flags config_flags = { 0 };
05564    const char *val;
05565 
05566    if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags))) {
05567       return;
05568    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
05569       ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format.  Aborting.\n");
05570       return;
05571    }
05572 
05573    audio_buffers = DEFAULT_AUDIO_BUFFERS;
05574 
05575    /*  Scheduling support is off by default */
05576    rt_schedule = 0;
05577    fuzzystart = 0;
05578    earlyalert = 0;
05579    endalert = 0;
05580    extendby = 0;
05581 
05582    /*  Logging of participants defaults to ON for compatibility reasons */
05583    rt_log_members = 1;  
05584 
05585    if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
05586       if ((sscanf(val, "%30d", &audio_buffers) != 1)) {
05587          ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
05588          audio_buffers = DEFAULT_AUDIO_BUFFERS;
05589       } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
05590          ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
05591             DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
05592          audio_buffers = DEFAULT_AUDIO_BUFFERS;
05593       }
05594       if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
05595          ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
05596    }
05597 
05598    if ((val = ast_variable_retrieve(cfg, "general", "schedule")))
05599       rt_schedule = ast_true(val);
05600    if ((val = ast_variable_retrieve(cfg, "general", "logmembercount")))
05601       rt_log_members = ast_true(val);
05602    if ((val = ast_variable_retrieve(cfg, "general", "fuzzystart"))) {
05603       if ((sscanf(val, "%30d", &fuzzystart) != 1)) {
05604          ast_log(LOG_WARNING, "fuzzystart must be a number, not '%s'\n", val);
05605          fuzzystart = 0;
05606       } 
05607    }
05608    if ((val = ast_variable_retrieve(cfg, "general", "earlyalert"))) {
05609       if ((sscanf(val, "%30d", &earlyalert) != 1)) {
05610          ast_log(LOG_WARNING, "earlyalert must be a number, not '%s'\n", val);
05611          earlyalert = 0;
05612       } 
05613    }
05614    if ((val = ast_variable_retrieve(cfg, "general", "endalert"))) {
05615       if ((sscanf(val, "%30d", &endalert) != 1)) {
05616          ast_log(LOG_WARNING, "endalert must be a number, not '%s'\n", val);
05617          endalert = 0;
05618       } 
05619    }
05620    if ((val = ast_variable_retrieve(cfg, "general", "extendby"))) {
05621       if ((sscanf(val, "%30d", &extendby) != 1)) {
05622          ast_log(LOG_WARNING, "extendby must be a number, not '%s'\n", val);
05623          extendby = 0;
05624       } 
05625    }
05626 
05627    ast_config_destroy(cfg);
05628 }
05629 
05630 /*!
05631  * \internal
05632  * \brief Find an SLA trunk by name
05633  */
05634 static struct sla_trunk *sla_find_trunk(const char *name)
05635 {
05636    struct sla_trunk tmp_trunk = {
05637       .name = name,
05638    };
05639 
05640    return ao2_find(sla_trunks, &tmp_trunk, OBJ_POINTER);
05641 }
05642 
05643 /*!
05644  * \internal
05645  * \brief Find an SLA station by name
05646  */
05647 static struct sla_station *sla_find_station(const char *name)
05648 {
05649    struct sla_station tmp_station = {
05650       .name = name,
05651    };
05652 
05653    return ao2_find(sla_stations, &tmp_station, OBJ_POINTER);
05654 }
05655 
05656 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
05657    const struct sla_station *station)
05658 {
05659    struct sla_station_ref *station_ref;
05660    struct sla_trunk_ref *trunk_ref;
05661 
05662    /* For each station that has this call on hold, check for private hold. */
05663    AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
05664       AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
05665          if (trunk_ref->trunk != trunk || station_ref->station == station)
05666             continue;
05667          if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
05668             station_ref->station->hold_access == SLA_HOLD_PRIVATE)
05669             return 1;
05670          return 0;
05671       }
05672    }
05673 
05674    return 0;
05675 }
05676 
05677 /*!
05678  * \brief Find a trunk reference on a station by name
05679  * \param station the station
05680  * \param name the trunk's name
05681  * \pre sla_station is locked
05682  * \return a pointer to the station's trunk reference.  If the trunk
05683  *         is not found, it is not idle and barge is disabled, or if
05684  *         it is on hold and private hold is set, then NULL will be returned.
05685  */
05686 static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
05687    const char *name)
05688 {
05689    struct sla_trunk_ref *trunk_ref = NULL;
05690 
05691    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05692       if (strcasecmp(trunk_ref->trunk->name, name))
05693          continue;
05694 
05695       if ( (trunk_ref->trunk->barge_disabled 
05696          && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
05697          (trunk_ref->trunk->hold_stations 
05698          && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
05699          && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
05700          sla_check_station_hold_access(trunk_ref->trunk, station) ) 
05701       {
05702          trunk_ref = NULL;
05703       }
05704 
05705       break;
05706    }
05707 
05708    if (trunk_ref) {
05709       ao2_ref(trunk_ref, 1);
05710    }
05711 
05712    return trunk_ref;
05713 }
05714 
05715 static void sla_station_ref_destructor(void *obj)
05716 {
05717    struct sla_station_ref *station_ref = obj;
05718 
05719    if (station_ref->station) {
05720       ao2_ref(station_ref->station, -1);
05721       station_ref->station = NULL;
05722    }
05723 }
05724 
05725 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
05726 {
05727    struct sla_station_ref *station_ref;
05728 
05729    if (!(station_ref = ao2_alloc(sizeof(*station_ref), sla_station_ref_destructor))) {
05730       return NULL;
05731    }
05732 
05733    ao2_ref(station, 1);
05734    station_ref->station = station;
05735 
05736    return station_ref;
05737 }
05738 
05739 static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
05740 {
05741    struct sla_ringing_station *ringing_station;
05742 
05743    if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
05744       return NULL;
05745 
05746    ao2_ref(station, 1);
05747    ringing_station->station = station;
05748    ringing_station->ring_begin = ast_tvnow();
05749 
05750    return ringing_station;
05751 }
05752 
05753 static void sla_ringing_station_destroy(struct sla_ringing_station *ringing_station)
05754 {
05755    if (ringing_station->station) {
05756       ao2_ref(ringing_station->station, -1);
05757       ringing_station->station = NULL;
05758    }
05759 
05760    ast_free(ringing_station);
05761 }
05762 
05763 static struct sla_failed_station *sla_create_failed_station(struct sla_station *station)
05764 {
05765    struct sla_failed_station *failed_station;
05766 
05767    if (!(failed_station = ast_calloc(1, sizeof(*failed_station)))) {
05768       return NULL;
05769    }
05770 
05771    ao2_ref(station, 1);
05772    failed_station->station = station;
05773    failed_station->last_try = ast_tvnow();
05774 
05775    return failed_station;
05776 }
05777 
05778 static void sla_failed_station_destroy(struct sla_failed_station *failed_station)
05779 {
05780    if (failed_station->station) {
05781       ao2_ref(failed_station->station, -1);
05782       failed_station->station = NULL;
05783    }
05784 
05785    ast_free(failed_station);
05786 }
05787 
05788 static enum ast_device_state sla_state_to_devstate(enum sla_trunk_state state)
05789 {
05790    switch (state) {
05791    case SLA_TRUNK_STATE_IDLE:
05792       return AST_DEVICE_NOT_INUSE;
05793    case SLA_TRUNK_STATE_RINGING:
05794       return AST_DEVICE_RINGING;
05795    case SLA_TRUNK_STATE_UP:
05796       return AST_DEVICE_INUSE;
05797    case SLA_TRUNK_STATE_ONHOLD:
05798    case SLA_TRUNK_STATE_ONHOLD_BYME:
05799       return AST_DEVICE_ONHOLD;
05800    }
05801 
05802    return AST_DEVICE_UNKNOWN;
05803 }
05804 
05805 static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state, 
05806    enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
05807 {
05808    struct sla_station *station;
05809    struct sla_trunk_ref *trunk_ref;
05810    struct ao2_iterator i;
05811 
05812    i = ao2_iterator_init(sla_stations, 0);
05813    while ((station = ao2_iterator_next(&i))) {
05814       ao2_lock(station);
05815       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05816          if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
05817                || trunk_ref == exclude) {
05818             continue;
05819          }
05820          trunk_ref->state = state;
05821          ast_devstate_changed(sla_state_to_devstate(state), AST_DEVSTATE_CACHABLE,
05822                     "SLA:%s_%s", station->name, trunk->name);
05823          break;
05824       }
05825       ao2_unlock(station);
05826       ao2_ref(station, -1);
05827    }
05828    ao2_iterator_destroy(&i);
05829 }
05830 
05831 struct run_station_args {
05832    struct sla_station *station;
05833    struct sla_trunk_ref *trunk_ref;
05834    ast_mutex_t *cond_lock;
05835    ast_cond_t *cond;
05836 };
05837 
05838 static void answer_trunk_chan(struct ast_channel *chan)
05839 {
05840    ast_answer(chan);
05841    ast_indicate(chan, -1);
05842 }
05843 
05844 static void *run_station(void *data)
05845 {
05846    RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
05847    RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
05848    struct ast_str *conf_name = ast_str_create(16);
05849    struct ast_flags64 conf_flags = { 0 };
05850    struct ast_conference *conf;
05851 
05852    {
05853       struct run_station_args *args = data;
05854       station = args->station;
05855       trunk_ref = args->trunk_ref;
05856       ast_mutex_lock(args->cond_lock);
05857       ast_cond_signal(args->cond);
05858       ast_mutex_unlock(args->cond_lock);
05859       /* args is no longer valid here. */
05860    }
05861 
05862    ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
05863    ast_str_set(&conf_name, 0, "SLA_%s", trunk_ref->trunk->name);
05864    ast_set_flag64(&conf_flags, 
05865       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
05866    answer_trunk_chan(trunk_ref->chan);
05867    conf = build_conf(ast_str_buffer(conf_name), "", "", 0, 0, 1, trunk_ref->chan, NULL);
05868    if (conf) {
05869       conf_run(trunk_ref->chan, conf, &conf_flags, NULL);
05870       dispose_conf(conf);
05871       conf = NULL;
05872    }
05873    trunk_ref->chan = NULL;
05874    if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
05875       trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
05876       ast_str_append(&conf_name, 0, ",K");
05877       admin_exec(NULL, ast_str_buffer(conf_name));
05878       trunk_ref->trunk->hold_stations = 0;
05879       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05880    }
05881 
05882    ast_dial_join(station->dial);
05883    ast_dial_destroy(station->dial);
05884    station->dial = NULL;
05885    ast_free(conf_name);
05886 
05887    return NULL;
05888 }
05889 
05890 static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk);
05891 
05892 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
05893 {
05894    char buf[80];
05895    struct sla_station_ref *station_ref;
05896 
05897    snprintf(buf, sizeof(buf), "SLA_%s,K", ringing_trunk->trunk->name);
05898    admin_exec(NULL, buf);
05899    sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05900 
05901    while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry))) {
05902       ao2_ref(station_ref, -1);
05903    }
05904 
05905    sla_ringing_trunk_destroy(ringing_trunk);
05906 }
05907 
05908 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
05909    enum sla_station_hangup hangup)
05910 {
05911    struct sla_ringing_trunk *ringing_trunk;
05912    struct sla_trunk_ref *trunk_ref;
05913    struct sla_station_ref *station_ref;
05914 
05915    ast_dial_join(ringing_station->station->dial);
05916    ast_dial_destroy(ringing_station->station->dial);
05917    ringing_station->station->dial = NULL;
05918 
05919    if (hangup == SLA_STATION_HANGUP_NORMAL)
05920       goto done;
05921 
05922    /* If the station is being hung up because of a timeout, then add it to the
05923     * list of timed out stations on each of the ringing trunks.  This is so
05924     * that when doing further processing to figure out which stations should be
05925     * ringing, which trunk to answer, determining timeouts, etc., we know which
05926     * ringing trunks we should ignore. */
05927    AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05928       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
05929          if (ringing_trunk->trunk == trunk_ref->trunk)
05930             break;
05931       }
05932       if (!trunk_ref)
05933          continue;
05934       if (!(station_ref = sla_create_station_ref(ringing_station->station)))
05935          continue;
05936       AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
05937    }
05938 
05939 done:
05940    sla_ringing_station_destroy(ringing_station);
05941 }
05942 
05943 static void sla_dial_state_callback(struct ast_dial *dial)
05944 {
05945    sla_queue_event(SLA_EVENT_DIAL_STATE);
05946 }
05947 
05948 /*! \brief Check to see if dialing this station already timed out for this ringing trunk
05949  * \note Assumes sla.lock is locked
05950  */
05951 static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
05952    const struct sla_station *station)
05953 {
05954    struct sla_station_ref *timed_out_station;
05955 
05956    AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
05957       if (station == timed_out_station->station)
05958          return 1;
05959    }
05960 
05961    return 0;
05962 }
05963 
05964 /*! \brief Choose the highest priority ringing trunk for a station
05965  * \param station the station
05966  * \param rm remove the ringing trunk once selected
05967  * \param trunk_ref a place to store the pointer to this stations reference to
05968  *        the selected trunk
05969  * \return a pointer to the selected ringing trunk, or NULL if none found
05970  * \note Assumes that sla.lock is locked
05971  */
05972 static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station, 
05973    struct sla_trunk_ref **trunk_ref, int rm)
05974 {
05975    struct sla_trunk_ref *s_trunk_ref;
05976    struct sla_ringing_trunk *ringing_trunk = NULL;
05977 
05978    AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
05979       AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
05980          /* Make sure this is the trunk we're looking for */
05981          if (s_trunk_ref->trunk != ringing_trunk->trunk)
05982             continue;
05983 
05984          /* This trunk on the station is ringing.  But, make sure this station
05985           * didn't already time out while this trunk was ringing. */
05986          if (sla_check_timed_out_station(ringing_trunk, station))
05987             continue;
05988 
05989          if (rm)
05990             AST_LIST_REMOVE_CURRENT(entry);
05991 
05992          if (trunk_ref) {
05993             ao2_ref(s_trunk_ref, 1);
05994             *trunk_ref = s_trunk_ref;
05995          }
05996 
05997          break;
05998       }
05999       AST_LIST_TRAVERSE_SAFE_END;
06000    
06001       if (ringing_trunk)
06002          break;
06003    }
06004 
06005    return ringing_trunk;
06006 }
06007 
06008 static void sla_handle_dial_state_event(void)
06009 {
06010    struct sla_ringing_station *ringing_station;
06011 
06012    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
06013       RAII_VAR(struct sla_trunk_ref *, s_trunk_ref, NULL, ao2_cleanup);
06014       struct sla_ringing_trunk *ringing_trunk = NULL;
06015       struct run_station_args args;
06016       enum ast_dial_result dial_res;
06017       pthread_t dont_care;
06018       ast_mutex_t cond_lock;
06019       ast_cond_t cond;
06020 
06021       switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
06022       case AST_DIAL_RESULT_HANGUP:
06023       case AST_DIAL_RESULT_INVALID:
06024       case AST_DIAL_RESULT_FAILED:
06025       case AST_DIAL_RESULT_TIMEOUT:
06026       case AST_DIAL_RESULT_UNANSWERED:
06027          AST_LIST_REMOVE_CURRENT(entry);
06028          sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
06029          break;
06030       case AST_DIAL_RESULT_ANSWERED:
06031          AST_LIST_REMOVE_CURRENT(entry);
06032          /* Find the appropriate trunk to answer. */
06033          ast_mutex_lock(&sla.lock);
06034          ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
06035          ast_mutex_unlock(&sla.lock);
06036          if (!ringing_trunk) {
06037             /* This case happens in a bit of a race condition.  If two stations answer
06038              * the outbound call at the same time, the first one will get connected to
06039              * the trunk.  When the second one gets here, it will not see any trunks
06040              * ringing so we have no idea what to conect it to.  So, we just hang up
06041              * on it. */
06042             ast_debug(1, "Found no ringing trunk for station '%s' to answer!\n", ringing_station->station->name);
06043             ast_dial_join(ringing_station->station->dial);
06044             ast_dial_destroy(ringing_station->station->dial);
06045             ringing_station->station->dial = NULL;
06046             sla_ringing_station_destroy(ringing_station);
06047             break;
06048          }
06049          /* Track the channel that answered this trunk */
06050          s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
06051          /* Actually answer the trunk */
06052          answer_trunk_chan(ringing_trunk->trunk->chan);
06053          sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06054          /* Now, start a thread that will connect this station to the trunk.  The rest of
06055           * the code here sets up the thread and ensures that it is able to save the arguments
06056           * before they are no longer valid since they are allocated on the stack. */
06057          ao2_ref(s_trunk_ref, 1);
06058          args.trunk_ref = s_trunk_ref;
06059          ao2_ref(ringing_station->station, 1);
06060          args.station = ringing_station->station;
06061          args.cond = &cond;
06062          args.cond_lock = &cond_lock;
06063          sla_ringing_trunk_destroy(ringing_trunk);
06064          sla_ringing_station_destroy(ringing_station);
06065          ast_mutex_init(&cond_lock);
06066          ast_cond_init(&cond, NULL);
06067          ast_mutex_lock(&cond_lock);
06068          ast_pthread_create_detached_background(&dont_care, NULL, run_station, &args);
06069          ast_cond_wait(&cond, &cond_lock);
06070          ast_mutex_unlock(&cond_lock);
06071          ast_mutex_destroy(&cond_lock);
06072          ast_cond_destroy(&cond);
06073          break;
06074       case AST_DIAL_RESULT_TRYING:
06075       case AST_DIAL_RESULT_RINGING:
06076       case AST_DIAL_RESULT_PROGRESS:
06077       case AST_DIAL_RESULT_PROCEEDING:
06078          break;
06079       }
06080       if (dial_res == AST_DIAL_RESULT_ANSWERED) {
06081          /* Queue up reprocessing ringing trunks, and then ringing stations again */
06082          sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06083          sla_queue_event(SLA_EVENT_DIAL_STATE);
06084          break;
06085       }
06086    }
06087    AST_LIST_TRAVERSE_SAFE_END;
06088 }
06089 
06090 /*! \brief Check to see if this station is already ringing 
06091  * \note Assumes sla.lock is locked 
06092  */
06093 static int sla_check_ringing_station(const struct sla_station *station)
06094 {
06095    struct sla_ringing_station *ringing_station;
06096 
06097    AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
06098       if (station == ringing_station->station)
06099          return 1;
06100    }
06101 
06102    return 0;
06103 }
06104 
06105 /*! \brief Check to see if this station has failed to be dialed in the past minute
06106  * \note assumes sla.lock is locked
06107  */
06108 static int sla_check_failed_station(const struct sla_station *station)
06109 {
06110    struct sla_failed_station *failed_station;
06111    int res = 0;
06112 
06113    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
06114       if (station != failed_station->station)
06115          continue;
06116       if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
06117          AST_LIST_REMOVE_CURRENT(entry);
06118          sla_failed_station_destroy(failed_station);
06119          break;
06120       }
06121       res = 1;
06122    }
06123    AST_LIST_TRAVERSE_SAFE_END
06124 
06125    return res;
06126 }
06127 
06128 /*! \brief Ring a station
06129  * \note Assumes sla.lock is locked
06130  */
06131 static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
06132 {
06133    char *tech, *tech_data;
06134    struct ast_dial *dial;
06135    struct sla_ringing_station *ringing_station;
06136    enum ast_dial_result res;
06137    int caller_is_saved;
06138    struct ast_party_caller caller;
06139 
06140    if (!(dial = ast_dial_create()))
06141       return -1;
06142 
06143    ast_dial_set_state_callback(dial, sla_dial_state_callback);
06144    tech_data = ast_strdupa(station->device);
06145    tech = strsep(&tech_data, "/");
06146 
06147    if (ast_dial_append(dial, tech, tech_data) == -1) {
06148       ast_dial_destroy(dial);
06149       return -1;
06150    }
06151 
06152    /* Do we need to save off the caller ID data? */
06153    caller_is_saved = 0;
06154    if (!sla.attempt_callerid) {
06155       caller_is_saved = 1;
06156       caller = *ast_channel_caller(ringing_trunk->trunk->chan);
06157       ast_party_caller_init(ast_channel_caller(ringing_trunk->trunk->chan));
06158    }
06159 
06160    res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
06161    
06162    /* Restore saved caller ID */
06163    if (caller_is_saved) {
06164       ast_party_caller_free(ast_channel_caller(ringing_trunk->trunk->chan));
06165       ast_channel_caller_set(ringing_trunk->trunk->chan, &caller);
06166    }
06167    
06168    if (res != AST_DIAL_RESULT_TRYING) {
06169       struct sla_failed_station *failed_station;
06170       ast_dial_destroy(dial);
06171       if ((failed_station = sla_create_failed_station(station))) {
06172          AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
06173       }
06174       return -1;
06175    }
06176    if (!(ringing_station = sla_create_ringing_station(station))) {
06177       ast_dial_join(dial);
06178       ast_dial_destroy(dial);
06179       return -1;
06180    }
06181 
06182    station->dial = dial;
06183 
06184    AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
06185 
06186    return 0;
06187 }
06188 
06189 /*! \brief Check to see if a station is in use
06190  */
06191 static int sla_check_inuse_station(const struct sla_station *station)
06192 {
06193    struct sla_trunk_ref *trunk_ref;
06194 
06195    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06196       if (trunk_ref->chan)
06197          return 1;
06198    }
06199 
06200    return 0;
06201 }
06202 
06203 static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
06204    const struct sla_trunk *trunk)
06205 {
06206    struct sla_trunk_ref *trunk_ref = NULL;
06207 
06208    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06209       if (trunk_ref->trunk == trunk)
06210          break;
06211    }
06212 
06213    ao2_ref(trunk_ref, 1);
06214 
06215    return trunk_ref;
06216 }
06217 
06218 /*! \brief Calculate the ring delay for a given ringing trunk on a station
06219  * \param station the station
06220  * \param ringing_trunk the trunk.  If NULL, the highest priority ringing trunk will be used
06221  * \return the number of ms left before the delay is complete, or INT_MAX if there is no delay
06222  */
06223 static int sla_check_station_delay(struct sla_station *station, 
06224    struct sla_ringing_trunk *ringing_trunk)
06225 {
06226    RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
06227    unsigned int delay = UINT_MAX;
06228    int time_left, time_elapsed;
06229 
06230    if (!ringing_trunk)
06231       ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
06232    else
06233       trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
06234 
06235    if (!ringing_trunk || !trunk_ref)
06236       return delay;
06237 
06238    /* If this station has a ring delay specific to the highest priority
06239     * ringing trunk, use that.  Otherwise, use the ring delay specified
06240     * globally for the station. */
06241    delay = trunk_ref->ring_delay;
06242    if (!delay)
06243       delay = station->ring_delay;
06244    if (!delay)
06245       return INT_MAX;
06246 
06247    time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
06248    time_left = (delay * 1000) - time_elapsed;
06249 
06250    return time_left;
06251 }
06252 
06253 /*! \brief Ring stations based on current set of ringing trunks
06254  * \note Assumes that sla.lock is locked
06255  */
06256 static void sla_ring_stations(void)
06257 {
06258    struct sla_station_ref *station_ref;
06259    struct sla_ringing_trunk *ringing_trunk;
06260 
06261    /* Make sure that every station that uses at least one of the ringing
06262     * trunks, is ringing. */
06263    AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
06264       AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
06265          int time_left;
06266 
06267          /* Is this station already ringing? */
06268          if (sla_check_ringing_station(station_ref->station))
06269             continue;
06270 
06271          /* Is this station already in a call? */
06272          if (sla_check_inuse_station(station_ref->station))
06273             continue;
06274 
06275          /* Did we fail to dial this station earlier?  If so, has it been
06276           * a minute since we tried? */
06277          if (sla_check_failed_station(station_ref->station))
06278             continue;
06279 
06280          /* If this station already timed out while this trunk was ringing,
06281           * do not dial it again for this ringing trunk. */
06282          if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
06283             continue;
06284 
06285          /* Check for a ring delay in progress */
06286          time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
06287          if (time_left != INT_MAX && time_left > 0)
06288             continue;
06289 
06290          /* It is time to make this station begin to ring.  Do it! */
06291          sla_ring_station(ringing_trunk, station_ref->station);
06292       }
06293    }
06294    /* Now, all of the stations that should be ringing, are ringing. */
06295 }
06296 
06297 static void sla_hangup_stations(void)
06298 {
06299    struct sla_trunk_ref *trunk_ref;
06300    struct sla_ringing_station *ringing_station;
06301 
06302    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
06303       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
06304          struct sla_ringing_trunk *ringing_trunk;
06305          ast_mutex_lock(&sla.lock);
06306          AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
06307             if (trunk_ref->trunk == ringing_trunk->trunk)
06308                break;
06309          }
06310          ast_mutex_unlock(&sla.lock);
06311          if (ringing_trunk)
06312             break;
06313       }
06314       if (!trunk_ref) {
06315          AST_LIST_REMOVE_CURRENT(entry);
06316          ast_dial_join(ringing_station->station->dial);
06317          ast_dial_destroy(ringing_station->station->dial);
06318          ringing_station->station->dial = NULL;
06319          sla_ringing_station_destroy(ringing_station);
06320       }
06321    }
06322    AST_LIST_TRAVERSE_SAFE_END
06323 }
06324 
06325 static void sla_handle_ringing_trunk_event(void)
06326 {
06327    ast_mutex_lock(&sla.lock);
06328    sla_ring_stations();
06329    ast_mutex_unlock(&sla.lock);
06330 
06331    /* Find stations that shouldn't be ringing anymore. */
06332    sla_hangup_stations();
06333 }
06334 
06335 static void sla_handle_hold_event(struct sla_event *event)
06336 {
06337    ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
06338    event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
06339    ast_devstate_changed(AST_DEVICE_ONHOLD, AST_DEVSTATE_CACHABLE, "SLA:%s_%s",
06340               event->station->name, event->trunk_ref->trunk->name);
06341    sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD, 
06342       INACTIVE_TRUNK_REFS, event->trunk_ref);
06343 
06344    if (event->trunk_ref->trunk->active_stations == 1) {
06345       /* The station putting it on hold is the only one on the call, so start
06346        * Music on hold to the trunk. */
06347       event->trunk_ref->trunk->on_hold = 1;
06348       ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
06349    }
06350 
06351    ast_softhangup(event->trunk_ref->chan, AST_SOFTHANGUP_DEV);
06352    event->trunk_ref->chan = NULL;
06353 }
06354 
06355 /*! \brief Process trunk ring timeouts
06356  * \note Called with sla.lock locked
06357  * \return non-zero if a change to the ringing trunks was made
06358  */
06359 static int sla_calc_trunk_timeouts(unsigned int *timeout)
06360 {
06361    struct sla_ringing_trunk *ringing_trunk;
06362    int res = 0;
06363 
06364    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06365       int time_left, time_elapsed;
06366       if (!ringing_trunk->trunk->ring_timeout)
06367          continue;
06368       time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
06369       time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
06370       if (time_left <= 0) {
06371          pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
06372          AST_LIST_REMOVE_CURRENT(entry);
06373          sla_stop_ringing_trunk(ringing_trunk);
06374          res = 1;
06375          continue;
06376       }
06377       if (time_left < *timeout)
06378          *timeout = time_left;
06379    }
06380    AST_LIST_TRAVERSE_SAFE_END;
06381 
06382    return res;
06383 }
06384 
06385 /*! \brief Process station ring timeouts
06386  * \note Called with sla.lock locked
06387  * \return non-zero if a change to the ringing stations was made
06388  */
06389 static int sla_calc_station_timeouts(unsigned int *timeout)
06390 {
06391    struct sla_ringing_trunk *ringing_trunk;
06392    struct sla_ringing_station *ringing_station;
06393    int res = 0;
06394 
06395    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
06396       unsigned int ring_timeout = 0;
06397       int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
06398       struct sla_trunk_ref *trunk_ref;
06399 
06400       /* If there are any ring timeouts specified for a specific trunk
06401        * on the station, then use the highest per-trunk ring timeout.
06402        * Otherwise, use the ring timeout set for the entire station. */
06403       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
06404          struct sla_station_ref *station_ref;
06405          int trunk_time_elapsed, trunk_time_left;
06406 
06407          AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
06408             if (ringing_trunk->trunk == trunk_ref->trunk)
06409                break;
06410          }
06411          if (!ringing_trunk)
06412             continue;
06413 
06414          /* If there is a trunk that is ringing without a timeout, then the
06415           * only timeout that could matter is a global station ring timeout. */
06416          if (!trunk_ref->ring_timeout)
06417             break;
06418 
06419          /* This trunk on this station is ringing and has a timeout.
06420           * However, make sure this trunk isn't still ringing from a
06421           * previous timeout.  If so, don't consider it. */
06422          AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
06423             if (station_ref->station == ringing_station->station)
06424                break;
06425          }
06426          if (station_ref)
06427             continue;
06428 
06429          trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
06430          trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
06431          if (trunk_time_left > final_trunk_time_left)
06432             final_trunk_time_left = trunk_time_left;
06433       }
06434 
06435       /* No timeout was found for ringing trunks, and no timeout for the entire station */
06436       if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
06437          continue;
06438 
06439       /* Compute how much time is left for a global station timeout */
06440       if (ringing_station->station->ring_timeout) {
06441          ring_timeout = ringing_station->station->ring_timeout;
06442          time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
06443          time_left = (ring_timeout * 1000) - time_elapsed;
06444       }
06445 
06446       /* If the time left based on the per-trunk timeouts is smaller than the
06447        * global station ring timeout, use that. */
06448       if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
06449          time_left = final_trunk_time_left;
06450 
06451       /* If there is no time left, the station needs to stop ringing */
06452       if (time_left <= 0) {
06453          AST_LIST_REMOVE_CURRENT(entry);
06454          sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
06455          res = 1;
06456          continue;
06457       }
06458 
06459       /* There is still some time left for this station to ring, so save that
06460        * timeout if it is the first event scheduled to occur */
06461       if (time_left < *timeout)
06462          *timeout = time_left;
06463    }
06464    AST_LIST_TRAVERSE_SAFE_END;
06465 
06466    return res;
06467 }
06468 
06469 /*! \brief Calculate the ring delay for a station
06470  * \note Assumes sla.lock is locked
06471  */
06472 static int sla_calc_station_delays(unsigned int *timeout)
06473 {
06474    struct sla_station *station;
06475    int res = 0;
06476    struct ao2_iterator i;
06477 
06478    i = ao2_iterator_init(sla_stations, 0);
06479    for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
06480       struct sla_ringing_trunk *ringing_trunk;
06481       int time_left;
06482 
06483       /* Ignore stations already ringing */
06484       if (sla_check_ringing_station(station))
06485          continue;
06486 
06487       /* Ignore stations already on a call */
06488       if (sla_check_inuse_station(station))
06489          continue;
06490 
06491       /* Ignore stations that don't have one of their trunks ringing */
06492       if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
06493          continue;
06494 
06495       if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
06496          continue;
06497 
06498       /* If there is no time left, then the station needs to start ringing.
06499        * Return non-zero so that an event will be queued up an event to 
06500        * make that happen. */
06501       if (time_left <= 0) {
06502          res = 1;
06503          continue;
06504       }
06505 
06506       if (time_left < *timeout)
06507          *timeout = time_left;
06508    }
06509    ao2_iterator_destroy(&i);
06510 
06511    return res;
06512 }
06513 
06514 /*! \brief Calculate the time until the next known event
06515  *  \note Called with sla.lock locked */
06516 static int sla_process_timers(struct timespec *ts)
06517 {
06518    unsigned int timeout = UINT_MAX;
06519    struct timeval wait;
06520    unsigned int change_made = 0;
06521 
06522    /* Check for ring timeouts on ringing trunks */
06523    if (sla_calc_trunk_timeouts(&timeout))
06524       change_made = 1;
06525 
06526    /* Check for ring timeouts on ringing stations */
06527    if (sla_calc_station_timeouts(&timeout))
06528       change_made = 1;
06529 
06530    /* Check for station ring delays */
06531    if (sla_calc_station_delays(&timeout))
06532       change_made = 1;
06533 
06534    /* queue reprocessing of ringing trunks */
06535    if (change_made)
06536       sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
06537 
06538    /* No timeout */
06539    if (timeout == UINT_MAX)
06540       return 0;
06541 
06542    if (ts) {
06543       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
06544       ts->tv_sec = wait.tv_sec;
06545       ts->tv_nsec = wait.tv_usec * 1000;
06546    }
06547 
06548    return 1;
06549 }
06550 
06551 static void sla_event_destroy(struct sla_event *event)
06552 {
06553    if (event->trunk_ref) {
06554       ao2_ref(event->trunk_ref, -1);
06555       event->trunk_ref = NULL;
06556    }
06557 
06558    if (event->station) {
06559       ao2_ref(event->station, -1);
06560       event->station = NULL;
06561    }
06562 
06563    ast_free(event);
06564 }
06565 
06566 static void *sla_thread(void *data)
06567 {
06568    struct sla_failed_station *failed_station;
06569    struct sla_ringing_station *ringing_station;
06570 
06571    ast_mutex_lock(&sla.lock);
06572 
06573    while (!sla.stop) {
06574       struct sla_event *event;
06575       struct timespec ts = { 0, };
06576       unsigned int have_timeout = 0;
06577 
06578       if (AST_LIST_EMPTY(&sla.event_q)) {
06579          if ((have_timeout = sla_process_timers(&ts)))
06580             ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
06581          else
06582             ast_cond_wait(&sla.cond, &sla.lock);
06583          if (sla.stop)
06584             break;
06585       }
06586 
06587       if (have_timeout)
06588          sla_process_timers(NULL);
06589 
06590       while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
06591          ast_mutex_unlock(&sla.lock);
06592          switch (event->type) {
06593          case SLA_EVENT_HOLD:
06594             sla_handle_hold_event(event);
06595             break;
06596          case SLA_EVENT_DIAL_STATE:
06597             sla_handle_dial_state_event();
06598             break;
06599          case SLA_EVENT_RINGING_TRUNK:
06600             sla_handle_ringing_trunk_event();
06601             break;
06602          }
06603          sla_event_destroy(event);
06604          ast_mutex_lock(&sla.lock);
06605       }
06606    }
06607 
06608    ast_mutex_unlock(&sla.lock);
06609 
06610    while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry))) {
06611       sla_ringing_station_destroy(ringing_station);
06612    }
06613 
06614    while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry))) {
06615       sla_failed_station_destroy(failed_station);
06616    }
06617 
06618    return NULL;
06619 }
06620 
06621 struct dial_trunk_args {
06622    struct sla_trunk_ref *trunk_ref;
06623    struct sla_station *station;
06624    ast_mutex_t *cond_lock;
06625    ast_cond_t *cond;
06626 };
06627 
06628 static void *dial_trunk(void *data)
06629 {
06630    struct dial_trunk_args *args = data;
06631    struct ast_dial *dial;
06632    char *tech, *tech_data;
06633    enum ast_dial_result dial_res;
06634    char conf_name[MAX_CONFNUM];
06635    struct ast_conference *conf;
06636    struct ast_flags64 conf_flags = { 0 };
06637    RAII_VAR(struct sla_trunk_ref *, trunk_ref, args->trunk_ref, ao2_cleanup);
06638    RAII_VAR(struct sla_station *, station, args->station, ao2_cleanup);
06639    int caller_is_saved;
06640    struct ast_party_caller caller;
06641    int last_state = 0;
06642    int current_state = 0;
06643 
06644    if (!(dial = ast_dial_create())) {
06645       ast_mutex_lock(args->cond_lock);
06646       ast_cond_signal(args->cond);
06647       ast_mutex_unlock(args->cond_lock);
06648       return NULL;
06649    }
06650 
06651    tech_data = ast_strdupa(trunk_ref->trunk->device);
06652    tech = strsep(&tech_data, "/");
06653    if (ast_dial_append(dial, tech, tech_data) == -1) {
06654       ast_mutex_lock(args->cond_lock);
06655       ast_cond_signal(args->cond);
06656       ast_mutex_unlock(args->cond_lock);
06657       ast_dial_destroy(dial);
06658       return NULL;
06659    }
06660 
06661    /* Do we need to save of the caller ID data? */
06662    caller_is_saved = 0;
06663    if (!sla.attempt_callerid) {
06664       caller_is_saved = 1;
06665       caller = *ast_channel_caller(trunk_ref->chan);
06666       ast_party_caller_init(ast_channel_caller(trunk_ref->chan));
06667    }
06668 
06669    dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
06670 
06671    /* Restore saved caller ID */
06672    if (caller_is_saved) {
06673       ast_party_caller_free(ast_channel_caller(trunk_ref->chan));
06674       ast_channel_caller_set(trunk_ref->chan, &caller);
06675    }
06676 
06677    if (dial_res != AST_DIAL_RESULT_TRYING) {
06678       ast_mutex_lock(args->cond_lock);
06679       ast_cond_signal(args->cond);
06680       ast_mutex_unlock(args->cond_lock);
06681       ast_dial_destroy(dial);
06682       return NULL;
06683    }
06684 
06685    for (;;) {
06686       unsigned int done = 0;
06687       switch ((dial_res = ast_dial_state(dial))) {
06688       case AST_DIAL_RESULT_ANSWERED:
06689          trunk_ref->trunk->chan = ast_dial_answered(dial);
06690       case AST_DIAL_RESULT_HANGUP:
06691       case AST_DIAL_RESULT_INVALID:
06692       case AST_DIAL_RESULT_FAILED:
06693       case AST_DIAL_RESULT_TIMEOUT:
06694       case AST_DIAL_RESULT_UNANSWERED:
06695          done = 1;
06696          break;
06697       case AST_DIAL_RESULT_TRYING:
06698          current_state = AST_CONTROL_PROGRESS;
06699          break;
06700       case AST_DIAL_RESULT_RINGING:
06701       case AST_DIAL_RESULT_PROGRESS:
06702       case AST_DIAL_RESULT_PROCEEDING:
06703          current_state = AST_CONTROL_RINGING;
06704          break;
06705       }
06706       if (done)
06707          break;
06708 
06709       /* check that SLA station that originated trunk call is still alive */
06710       if (station && ast_device_state(station->device) == AST_DEVICE_NOT_INUSE) {
06711          ast_debug(3, "Originating station device %s no longer active\n", station->device);
06712          trunk_ref->trunk->chan = NULL;
06713          break;
06714       }
06715 
06716       /* If trunk line state changed, send indication back to originating SLA Station channel */
06717       if (current_state != last_state) {
06718          ast_debug(3, "Indicating State Change %d to channel %s\n", current_state, ast_channel_name(trunk_ref->chan));
06719          ast_indicate(trunk_ref->chan, current_state);
06720          last_state = current_state;
06721       }
06722 
06723       /* avoid tight loop... sleep for 1/10th second */
06724       ast_safe_sleep(trunk_ref->chan, 100);
06725    }
06726 
06727    if (!trunk_ref->trunk->chan) {
06728       ast_mutex_lock(args->cond_lock);
06729       ast_cond_signal(args->cond);
06730       ast_mutex_unlock(args->cond_lock);
06731       ast_dial_join(dial);
06732       ast_dial_destroy(dial);
06733       return NULL;
06734    }
06735 
06736    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06737    ast_set_flag64(&conf_flags, 
06738       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | 
06739       CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
06740    conf = build_conf(conf_name, "", "", 1, 1, 1, trunk_ref->trunk->chan, NULL);
06741 
06742    ast_mutex_lock(args->cond_lock);
06743    ast_cond_signal(args->cond);
06744    ast_mutex_unlock(args->cond_lock);
06745 
06746    if (conf) {
06747       conf_run(trunk_ref->trunk->chan, conf, &conf_flags, NULL);
06748       dispose_conf(conf);
06749       conf = NULL;
06750    }
06751 
06752    /* If the trunk is going away, it is definitely now IDLE. */
06753    sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06754 
06755    trunk_ref->trunk->chan = NULL;
06756    trunk_ref->trunk->on_hold = 0;
06757 
06758    ast_dial_join(dial);
06759    ast_dial_destroy(dial);
06760 
06761    return NULL;
06762 }
06763 
06764 /*!
06765  * \brief For a given station, choose the highest priority idle trunk
06766  * \pre sla_station is locked
06767  */
06768 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
06769 {
06770    struct sla_trunk_ref *trunk_ref = NULL;
06771 
06772    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06773       if (trunk_ref->state == SLA_TRUNK_STATE_IDLE) {
06774          ao2_ref(trunk_ref, 1);
06775          break;
06776       }
06777    }
06778 
06779    return trunk_ref;
06780 }
06781 
06782 static int sla_station_exec(struct ast_channel *chan, const char *data)
06783 {
06784    char *station_name, *trunk_name;
06785    RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
06786    RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
06787    char conf_name[MAX_CONFNUM];
06788    struct ast_flags64 conf_flags = { 0 };
06789    struct ast_conference *conf;
06790 
06791    if (ast_strlen_zero(data)) {
06792       ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06793       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06794       return 0;
06795    }
06796 
06797    trunk_name = ast_strdupa(data);
06798    station_name = strsep(&trunk_name, "_");
06799 
06800    if (ast_strlen_zero(station_name)) {
06801       ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06802       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06803       return 0;
06804    }
06805 
06806    station = sla_find_station(station_name);
06807 
06808    if (!station) {
06809       ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
06810       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06811       return 0;
06812    }
06813 
06814    ao2_lock(station);
06815    if (!ast_strlen_zero(trunk_name)) {
06816       trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
06817    } else {
06818       trunk_ref = sla_choose_idle_trunk(station);
06819    }
06820    ao2_unlock(station);
06821 
06822    if (!trunk_ref) {
06823       if (ast_strlen_zero(trunk_name))
06824          ast_log(LOG_NOTICE, "No trunks available for call.\n");
06825       else {
06826          ast_log(LOG_NOTICE, "Can't join existing call on trunk "
06827             "'%s' due to access controls.\n", trunk_name);
06828       }
06829       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06830       return 0;
06831    }
06832 
06833    if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
06834       if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
06835          sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06836       else {
06837          trunk_ref->state = SLA_TRUNK_STATE_UP;
06838          ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE,
06839                     "SLA:%s_%s", station->name, trunk_ref->trunk->name);
06840       }
06841    } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
06842       struct sla_ringing_trunk *ringing_trunk;
06843 
06844       ast_mutex_lock(&sla.lock);
06845       AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06846          if (ringing_trunk->trunk == trunk_ref->trunk) {
06847             AST_LIST_REMOVE_CURRENT(entry);
06848             break;
06849          }
06850       }
06851       AST_LIST_TRAVERSE_SAFE_END
06852       ast_mutex_unlock(&sla.lock);
06853 
06854       if (ringing_trunk) {
06855          answer_trunk_chan(ringing_trunk->trunk->chan);
06856          sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06857 
06858          sla_ringing_trunk_destroy(ringing_trunk);
06859 
06860          /* Queue up reprocessing ringing trunks, and then ringing stations again */
06861          sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06862          sla_queue_event(SLA_EVENT_DIAL_STATE);
06863       }
06864    }
06865 
06866    trunk_ref->chan = chan;
06867 
06868    if (!trunk_ref->trunk->chan) {
06869       ast_mutex_t cond_lock;
06870       ast_cond_t cond;
06871       pthread_t dont_care;
06872       struct dial_trunk_args args = {
06873          .trunk_ref = trunk_ref,
06874          .station = station,
06875          .cond_lock = &cond_lock,
06876          .cond = &cond,
06877       };
06878       ao2_ref(trunk_ref, 1);
06879       ao2_ref(station, 1);
06880       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06881       /* Create a thread to dial the trunk and dump it into the conference.
06882        * However, we want to wait until the trunk has been dialed and the
06883        * conference is created before continuing on here. */
06884       ast_autoservice_start(chan);
06885       ast_mutex_init(&cond_lock);
06886       ast_cond_init(&cond, NULL);
06887       ast_mutex_lock(&cond_lock);
06888       ast_pthread_create_detached_background(&dont_care, NULL, dial_trunk, &args);
06889       ast_cond_wait(&cond, &cond_lock);
06890       ast_mutex_unlock(&cond_lock);
06891       ast_mutex_destroy(&cond_lock);
06892       ast_cond_destroy(&cond);
06893       ast_autoservice_stop(chan);
06894       if (!trunk_ref->trunk->chan) {
06895          ast_debug(1, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
06896          pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06897          sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06898          trunk_ref->chan = NULL;
06899          return 0;
06900       }
06901    }
06902 
06903    if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
06904       trunk_ref->trunk->on_hold) {
06905       trunk_ref->trunk->on_hold = 0;
06906       ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
06907       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06908    }
06909 
06910    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06911    ast_set_flag64(&conf_flags,
06912       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
06913    ast_answer(chan);
06914    conf = build_conf(conf_name, "", "", 0, 0, 1, chan, NULL);
06915    if (conf) {
06916       conf_run(chan, conf, &conf_flags, NULL);
06917       dispose_conf(conf);
06918       conf = NULL;
06919    }
06920    trunk_ref->chan = NULL;
06921    if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
06922       trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
06923       strncat(conf_name, ",K", sizeof(conf_name) - strlen(conf_name) - 1);
06924       admin_exec(NULL, conf_name);
06925       trunk_ref->trunk->hold_stations = 0;
06926       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06927    }
06928    
06929    pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
06930 
06931    return 0;
06932 }
06933 
06934 static void sla_trunk_ref_destructor(void *obj)
06935 {
06936    struct sla_trunk_ref *trunk_ref = obj;
06937 
06938    if (trunk_ref->trunk) {
06939       ao2_ref(trunk_ref->trunk, -1);
06940       trunk_ref->trunk = NULL;
06941    }
06942 }
06943 
06944 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
06945 {
06946    struct sla_trunk_ref *trunk_ref;
06947 
06948    if (!(trunk_ref = ao2_alloc(sizeof(*trunk_ref), sla_trunk_ref_destructor))) {
06949       return NULL;
06950    }
06951 
06952    ao2_ref(trunk, 1);
06953    trunk_ref->trunk = trunk;
06954 
06955    return trunk_ref;
06956 }
06957 
06958 static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
06959 {
06960    struct sla_ringing_trunk *ringing_trunk;
06961 
06962    if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk)))) {
06963       return NULL;
06964    }
06965 
06966    ao2_ref(trunk, 1);
06967    ringing_trunk->trunk = trunk;
06968    ringing_trunk->ring_begin = ast_tvnow();
06969 
06970    sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
06971 
06972    ast_mutex_lock(&sla.lock);
06973    AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
06974    ast_mutex_unlock(&sla.lock);
06975 
06976    sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06977 
06978    return ringing_trunk;
06979 }
06980 
06981 static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk)
06982 {
06983    if (ringing_trunk->trunk) {
06984       ao2_ref(ringing_trunk->trunk, -1);
06985       ringing_trunk->trunk = NULL;
06986    }
06987 
06988    ast_free(ringing_trunk);
06989 }
06990 
06991 enum {
06992    SLA_TRUNK_OPT_MOH = (1 << 0),
06993 };
06994 
06995 enum {
06996    SLA_TRUNK_OPT_ARG_MOH_CLASS = 0,
06997    SLA_TRUNK_OPT_ARG_ARRAY_SIZE = 1,
06998 };
06999 
07000 AST_APP_OPTIONS(sla_trunk_opts, BEGIN_OPTIONS
07001    AST_APP_OPTION_ARG('M', SLA_TRUNK_OPT_MOH, SLA_TRUNK_OPT_ARG_MOH_CLASS),
07002 END_OPTIONS );
07003 
07004 static int sla_trunk_exec(struct ast_channel *chan, const char *data)
07005 {
07006    char conf_name[MAX_CONFNUM];
07007    struct ast_conference *conf;
07008    struct ast_flags64 conf_flags = { 0 };
07009    RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
07010    struct sla_ringing_trunk *ringing_trunk;
07011    AST_DECLARE_APP_ARGS(args,
07012       AST_APP_ARG(trunk_name);
07013       AST_APP_ARG(options);
07014    );
07015    char *opts[SLA_TRUNK_OPT_ARG_ARRAY_SIZE] = { NULL, };
07016    struct ast_flags opt_flags = { 0 };
07017    char *parse;
07018 
07019    if (ast_strlen_zero(data)) {
07020       ast_log(LOG_ERROR, "The SLATrunk application requires an argument, the trunk name\n");
07021       return -1;
07022    }
07023 
07024    parse = ast_strdupa(data);
07025    AST_STANDARD_APP_ARGS(args, parse);
07026    if (args.argc == 2) {
07027       if (ast_app_parse_options(sla_trunk_opts, &opt_flags, opts, args.options)) {
07028          ast_log(LOG_ERROR, "Error parsing options for SLATrunk\n");
07029          return -1;
07030       }
07031    }
07032 
07033    trunk = sla_find_trunk(args.trunk_name);
07034 
07035    if (!trunk) {
07036       ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", args.trunk_name);
07037       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
07038       return 0;
07039    }
07040 
07041    if (trunk->chan) {
07042       ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
07043          args.trunk_name);
07044       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
07045       return 0;
07046    }
07047 
07048    trunk->chan = chan;
07049 
07050    if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
07051       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
07052       return 0;
07053    }
07054 
07055    snprintf(conf_name, sizeof(conf_name), "SLA_%s", args.trunk_name);
07056    conf = build_conf(conf_name, "", "", 1, 1, 1, chan, NULL);
07057    if (!conf) {
07058       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
07059       return 0;
07060    }
07061    ast_set_flag64(&conf_flags, 
07062       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF | CONFFLAG_NO_AUDIO_UNTIL_UP);
07063 
07064    if (ast_test_flag(&opt_flags, SLA_TRUNK_OPT_MOH)) {
07065       ast_indicate(chan, -1);
07066       ast_set_flag64(&conf_flags, CONFFLAG_MOH);
07067    } else
07068       ast_indicate(chan, AST_CONTROL_RINGING);
07069 
07070    conf_run(chan, conf, &conf_flags, opts);
07071    dispose_conf(conf);
07072    conf = NULL;
07073    trunk->chan = NULL;
07074    trunk->on_hold = 0;
07075 
07076    sla_change_trunk_state(trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
07077 
07078    if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
07079       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
07080 
07081    /* Remove the entry from the list of ringing trunks if it is still there. */
07082    ast_mutex_lock(&sla.lock);
07083    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
07084       if (ringing_trunk->trunk == trunk) {
07085          AST_LIST_REMOVE_CURRENT(entry);
07086          break;
07087       }
07088    }
07089    AST_LIST_TRAVERSE_SAFE_END;
07090    ast_mutex_unlock(&sla.lock);
07091    if (ringing_trunk) {
07092       sla_ringing_trunk_destroy(ringing_trunk);
07093       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
07094       /* Queue reprocessing of ringing trunks to make stations stop ringing
07095        * that shouldn't be ringing after this trunk stopped. */
07096       sla_queue_event(SLA_EVENT_RINGING_TRUNK);
07097    }
07098 
07099    return 0;
07100 }
07101 
07102 static enum ast_device_state sla_state(const char *data)
07103 {
07104    char *buf, *station_name, *trunk_name;
07105    RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
07106    struct sla_trunk_ref *trunk_ref;
07107    enum ast_device_state res = AST_DEVICE_INVALID;
07108 
07109    trunk_name = buf = ast_strdupa(data);
07110    station_name = strsep(&trunk_name, "_");
07111 
07112    station = sla_find_station(station_name);
07113    if (station) {
07114       ao2_lock(station);
07115       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07116          if (!strcasecmp(trunk_name, trunk_ref->trunk->name)) {
07117             res = sla_state_to_devstate(trunk_ref->state);
07118             break;
07119          }
07120       }
07121       ao2_unlock(station);
07122    }
07123 
07124    if (res == AST_DEVICE_INVALID) {
07125       ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
07126          trunk_name, station_name);
07127    }
07128 
07129    return res;
07130 }
07131 
07132 static int sla_trunk_release_refs(void *obj, void *arg, int flags)
07133 {
07134    struct sla_trunk *trunk = obj;
07135    struct sla_station_ref *station_ref;
07136 
07137    while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry))) {
07138       ao2_ref(station_ref, -1);
07139    }
07140 
07141    return 0;
07142 }
07143 
07144 static int sla_station_release_refs(void *obj, void *arg, int flags)
07145 {
07146    struct sla_station *station = obj;
07147    struct sla_trunk_ref *trunk_ref;
07148 
07149    while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry))) {
07150       ao2_ref(trunk_ref, -1);
07151    }
07152 
07153    return 0;
07154 }
07155 
07156 static void sla_station_destructor(void *obj)
07157 {
07158    struct sla_station *station = obj;
07159 
07160    ast_debug(1, "sla_station destructor for '%s'\n", station->name);
07161 
07162    if (!ast_strlen_zero(station->autocontext)) {
07163       struct sla_trunk_ref *trunk_ref;
07164 
07165       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07166          char exten[AST_MAX_EXTENSION];
07167          char hint[AST_MAX_APP];
07168          snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
07169          snprintf(hint, sizeof(hint), "SLA:%s", exten);
07170          ast_context_remove_extension(station->autocontext, exten, 
07171             1, sla_registrar);
07172          ast_context_remove_extension(station->autocontext, hint, 
07173             PRIORITY_HINT, sla_registrar);
07174       }
07175    }
07176 
07177    sla_station_release_refs(station, NULL, 0);
07178 
07179    ast_string_field_free_memory(station);
07180 }
07181 
07182 static int sla_trunk_hash(const void *obj, const int flags)
07183 {
07184    const struct sla_trunk *trunk = obj;
07185 
07186    return ast_str_case_hash(trunk->name);
07187 }
07188 
07189 static int sla_trunk_cmp(void *obj, void *arg, int flags)
07190 {
07191    struct sla_trunk *trunk = obj, *trunk2 = arg;
07192 
07193    return !strcasecmp(trunk->name, trunk2->name) ? CMP_MATCH | CMP_STOP : 0;
07194 }
07195 
07196 static int sla_station_hash(const void *obj, const int flags)
07197 {
07198    const struct sla_station *station = obj;
07199 
07200    return ast_str_case_hash(station->name);
07201 }
07202 
07203 static int sla_station_cmp(void *obj, void *arg, int flags)
07204 {
07205    struct sla_station *station = obj, *station2 = arg;
07206 
07207    return !strcasecmp(station->name, station2->name) ? CMP_MATCH | CMP_STOP : 0;
07208 }
07209 
07210 static void sla_destroy(void)
07211 {
07212    if (sla.thread != AST_PTHREADT_NULL) {
07213       ast_mutex_lock(&sla.lock);
07214       sla.stop = 1;
07215       ast_cond_signal(&sla.cond);
07216       ast_mutex_unlock(&sla.lock);
07217       pthread_join(sla.thread, NULL);
07218    }
07219 
07220    /* Drop any created contexts from the dialplan */
07221    ast_context_destroy(NULL, sla_registrar);
07222 
07223    ast_mutex_destroy(&sla.lock);
07224    ast_cond_destroy(&sla.cond);
07225 
07226    ao2_callback(sla_trunks, 0, sla_trunk_release_refs, NULL);
07227    ao2_callback(sla_stations, 0, sla_station_release_refs, NULL);
07228 
07229    ao2_ref(sla_trunks, -1);
07230    sla_trunks = NULL;
07231 
07232    ao2_ref(sla_stations, -1);
07233    sla_stations = NULL;
07234 }
07235 
07236 static int sla_check_device(const char *device)
07237 {
07238    char *tech, *tech_data;
07239 
07240    tech_data = ast_strdupa(device);
07241    tech = strsep(&tech_data, "/");
07242 
07243    if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
07244       return -1;
07245 
07246    return 0;
07247 }
07248 
07249 static void sla_trunk_destructor(void *obj)
07250 {
07251    struct sla_trunk *trunk = obj;
07252 
07253    ast_debug(1, "sla_trunk destructor for '%s'\n", trunk->name);
07254 
07255    if (!ast_strlen_zero(trunk->autocontext)) {
07256       ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
07257    }
07258 
07259    sla_trunk_release_refs(trunk, NULL, 0);
07260 
07261    ast_string_field_free_memory(trunk);
07262 }
07263 
07264 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
07265 {
07266    RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
07267    struct ast_variable *var;
07268    const char *dev;
07269    int existing_trunk = 0;
07270 
07271    if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
07272       ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
07273       return -1;
07274    }
07275 
07276    if (sla_check_device(dev)) {
07277       ast_log(LOG_ERROR, "SLA Trunk '%s' defined with invalid device '%s'!\n",
07278          cat, dev);
07279       return -1;
07280    }
07281 
07282    if ((trunk = sla_find_trunk(cat))) {
07283       trunk->mark = 0;
07284       existing_trunk = 1;
07285    } else if ((trunk = ao2_alloc(sizeof(*trunk), sla_trunk_destructor))) {
07286       if (ast_string_field_init(trunk, 32)) {
07287          return -1;
07288       }
07289       ast_string_field_set(trunk, name, cat);
07290    } else {
07291       return -1;
07292    }
07293 
07294    ao2_lock(trunk);
07295 
07296    ast_string_field_set(trunk, device, dev);
07297 
07298    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
07299       if (!strcasecmp(var->name, "autocontext"))
07300          ast_string_field_set(trunk, autocontext, var->value);
07301       else if (!strcasecmp(var->name, "ringtimeout")) {
07302          if (sscanf(var->value, "%30u", &trunk->ring_timeout) != 1) {
07303             ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
07304                var->value, trunk->name);
07305             trunk->ring_timeout = 0;
07306          }
07307       } else if (!strcasecmp(var->name, "barge"))
07308          trunk->barge_disabled = ast_false(var->value);
07309       else if (!strcasecmp(var->name, "hold")) {
07310          if (!strcasecmp(var->value, "private"))
07311             trunk->hold_access = SLA_HOLD_PRIVATE;
07312          else if (!strcasecmp(var->value, "open"))
07313             trunk->hold_access = SLA_HOLD_OPEN;
07314          else {
07315             ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
07316                var->value, trunk->name);
07317          }
07318       } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
07319          ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
07320             var->name, var->lineno, SLA_CONFIG_FILE);
07321       }
07322    }
07323 
07324    ao2_unlock(trunk);
07325 
07326    if (!ast_strlen_zero(trunk->autocontext)) {
07327       struct ast_context *context;
07328       context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
07329       if (!context) {
07330          ast_log(LOG_ERROR, "Failed to automatically find or create "
07331             "context '%s' for SLA!\n", trunk->autocontext);
07332          return -1;
07333       }
07334       if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
07335          NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
07336          ast_log(LOG_ERROR, "Failed to automatically create extension "
07337             "for trunk '%s'!\n", trunk->name);
07338          return -1;
07339       }
07340    }
07341 
07342    if (!existing_trunk) {
07343       ao2_link(sla_trunks, trunk);
07344    }
07345 
07346    return 0;
07347 }
07348 
07349 /*!
07350  * \internal
07351  * \pre station is not locked
07352  */
07353 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
07354 {
07355    RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
07356    struct sla_trunk_ref *trunk_ref = NULL;
07357    struct sla_station_ref *station_ref;
07358    char *trunk_name, *options, *cur;
07359    int existing_trunk_ref = 0;
07360    int existing_station_ref = 0;
07361 
07362    options = ast_strdupa(var->value);
07363    trunk_name = strsep(&options, ",");
07364 
07365    trunk = sla_find_trunk(trunk_name);
07366    if (!trunk) {
07367       ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
07368       return;
07369    }
07370 
07371    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07372       if (trunk_ref->trunk == trunk) {
07373          trunk_ref->mark = 0;
07374          existing_trunk_ref = 1;
07375          break;
07376       }
07377    }
07378 
07379    if (!trunk_ref && !(trunk_ref = create_trunk_ref(trunk))) {
07380       return;
07381    }
07382 
07383    trunk_ref->state = SLA_TRUNK_STATE_IDLE;
07384 
07385    while ((cur = strsep(&options, ","))) {
07386       char *name, *value = cur;
07387       name = strsep(&value, "=");
07388       if (!strcasecmp(name, "ringtimeout")) {
07389          if (sscanf(value, "%30u", &trunk_ref->ring_timeout) != 1) {
07390             ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
07391                "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
07392             trunk_ref->ring_timeout = 0;
07393          }
07394       } else if (!strcasecmp(name, "ringdelay")) {
07395          if (sscanf(value, "%30u", &trunk_ref->ring_delay) != 1) {
07396             ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
07397                "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
07398             trunk_ref->ring_delay = 0;
07399          }
07400       } else {
07401          ast_log(LOG_WARNING, "Invalid option '%s' for "
07402             "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
07403       }
07404    }
07405 
07406    AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
07407       if (station_ref->station == station) {
07408          station_ref->mark = 0;
07409          existing_station_ref = 1;
07410          break;
07411       }
07412    }
07413 
07414    if (!station_ref && !(station_ref = sla_create_station_ref(station))) {
07415       if (!existing_trunk_ref) {
07416          ao2_ref(trunk_ref, -1);
07417       } else {
07418          trunk_ref->mark = 1;
07419       }
07420       return;
07421    }
07422 
07423    if (!existing_station_ref) {
07424       ao2_lock(trunk);
07425       AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
07426       ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
07427       ao2_unlock(trunk);
07428    }
07429 
07430    if (!existing_trunk_ref) {
07431       ao2_lock(station);
07432       AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
07433       ao2_unlock(station);
07434    }
07435 }
07436 
07437 static int sla_build_station(struct ast_config *cfg, const char *cat)
07438 {
07439    RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
07440    struct ast_variable *var;
07441    const char *dev;
07442    int existing_station = 0;
07443 
07444    if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
07445       ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
07446       return -1;
07447    }
07448 
07449    if ((station = sla_find_station(cat))) {
07450       station->mark = 0;
07451       existing_station = 1;
07452    } else if ((station = ao2_alloc(sizeof(*station), sla_station_destructor))) {
07453       if (ast_string_field_init(station, 32)) {
07454          return -1;
07455       }
07456       ast_string_field_set(station, name, cat);
07457    } else {
07458       return -1;
07459    }
07460 
07461    ao2_lock(station);
07462 
07463    ast_string_field_set(station, device, dev);
07464 
07465    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
07466       if (!strcasecmp(var->name, "trunk")) {
07467          ao2_unlock(station);
07468          sla_add_trunk_to_station(station, var);
07469          ao2_lock(station);
07470       } else if (!strcasecmp(var->name, "autocontext")) {
07471          ast_string_field_set(station, autocontext, var->value);
07472       } else if (!strcasecmp(var->name, "ringtimeout")) {
07473          if (sscanf(var->value, "%30u", &station->ring_timeout) != 1) {
07474             ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
07475                var->value, station->name);
07476             station->ring_timeout = 0;
07477          }
07478       } else if (!strcasecmp(var->name, "ringdelay")) {
07479          if (sscanf(var->value, "%30u", &station->ring_delay) != 1) {
07480             ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
07481                var->value, station->name);
07482             station->ring_delay = 0;
07483          }
07484       } else if (!strcasecmp(var->name, "hold")) {
07485          if (!strcasecmp(var->value, "private"))
07486             station->hold_access = SLA_HOLD_PRIVATE;
07487          else if (!strcasecmp(var->value, "open"))
07488             station->hold_access = SLA_HOLD_OPEN;
07489          else {
07490             ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
07491                var->value, station->name);
07492          }
07493 
07494       } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
07495          ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
07496             var->name, var->lineno, SLA_CONFIG_FILE);
07497       }
07498    }
07499 
07500    ao2_unlock(station);
07501 
07502    if (!ast_strlen_zero(station->autocontext)) {
07503       struct ast_context *context;
07504       struct sla_trunk_ref *trunk_ref;
07505       context = ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar);
07506       if (!context) {
07507          ast_log(LOG_ERROR, "Failed to automatically find or create "
07508             "context '%s' for SLA!\n", station->autocontext);
07509          return -1;
07510       }
07511       /* The extension for when the handset goes off-hook.
07512        * exten => station1,1,SLAStation(station1) */
07513       if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
07514          NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
07515          ast_log(LOG_ERROR, "Failed to automatically create extension "
07516             "for trunk '%s'!\n", station->name);
07517          return -1;
07518       }
07519       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07520          char exten[AST_MAX_EXTENSION];
07521          char hint[AST_MAX_APP];
07522          snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
07523          snprintf(hint, sizeof(hint), "SLA:%s", exten);
07524          /* Extension for this line button 
07525           * exten => station1_line1,1,SLAStation(station1_line1) */
07526          if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
07527             NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
07528             ast_log(LOG_ERROR, "Failed to automatically create extension "
07529                "for trunk '%s'!\n", station->name);
07530             return -1;
07531          }
07532          /* Hint for this line button 
07533           * exten => station1_line1,hint,SLA:station1_line1 */
07534          if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
07535             NULL, NULL, hint, NULL, NULL, sla_registrar)) {
07536             ast_log(LOG_ERROR, "Failed to automatically create hint "
07537                "for trunk '%s'!\n", station->name);
07538             return -1;
07539          }
07540       }
07541    }
07542 
07543    if (!existing_station) {
07544       ao2_link(sla_stations, station);
07545    }
07546 
07547    return 0;
07548 }
07549 
07550 static int sla_trunk_mark(void *obj, void *arg, int flags)
07551 {
07552    struct sla_trunk *trunk = obj;
07553    struct sla_station_ref *station_ref;
07554 
07555    ao2_lock(trunk);
07556 
07557    trunk->mark = 1;
07558 
07559    AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
07560       station_ref->mark = 1;
07561    }
07562 
07563    ao2_unlock(trunk);
07564 
07565    return 0;
07566 }
07567 
07568 static int sla_station_mark(void *obj, void *arg, int flags)
07569 {
07570    struct sla_station *station = obj;
07571    struct sla_trunk_ref *trunk_ref;
07572 
07573    ao2_lock(station);
07574 
07575    station->mark = 1;
07576 
07577    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07578       trunk_ref->mark = 1;
07579    }
07580 
07581    ao2_unlock(station);
07582 
07583    return 0;
07584 }
07585 
07586 static int sla_trunk_is_marked(void *obj, void *arg, int flags)
07587 {
07588    struct sla_trunk *trunk = obj;
07589 
07590    ao2_lock(trunk);
07591 
07592    if (trunk->mark) {
07593       /* Only remove all of the station references if the trunk itself is going away */
07594       sla_trunk_release_refs(trunk, NULL, 0);
07595    } else {
07596       struct sla_station_ref *station_ref;
07597 
07598       /* Otherwise only remove references to stations no longer in the config */
07599       AST_LIST_TRAVERSE_SAFE_BEGIN(&trunk->stations, station_ref, entry) {
07600          if (!station_ref->mark) {
07601             continue;
07602          }
07603          AST_LIST_REMOVE_CURRENT(entry);
07604          ao2_ref(station_ref, -1);
07605       }
07606       AST_LIST_TRAVERSE_SAFE_END
07607    }
07608 
07609    ao2_unlock(trunk);
07610 
07611    return trunk->mark ? CMP_MATCH : 0;
07612 }
07613 
07614 static int sla_station_is_marked(void *obj, void *arg, int flags)
07615 {
07616    struct sla_station *station = obj;
07617 
07618    ao2_lock(station);
07619 
07620    if (station->mark) {
07621       /* Only remove all of the trunk references if the station itself is going away */
07622       sla_station_release_refs(station, NULL, 0);
07623    } else {
07624       struct sla_trunk_ref *trunk_ref;
07625 
07626       /* Otherwise only remove references to trunks no longer in the config */
07627       AST_LIST_TRAVERSE_SAFE_BEGIN(&station->trunks, trunk_ref, entry) {
07628          if (!trunk_ref->mark) {
07629             continue;
07630          }
07631          AST_LIST_REMOVE_CURRENT(entry);
07632          ao2_ref(trunk_ref, -1);
07633       }
07634       AST_LIST_TRAVERSE_SAFE_END
07635    }
07636 
07637    ao2_unlock(station);
07638 
07639    return station->mark ? CMP_MATCH : 0;
07640 }
07641 
07642 static int sla_in_use(void)
07643 {
07644    return ao2_container_count(sla_trunks) || ao2_container_count(sla_stations);
07645 }
07646 
07647 static int sla_load_config(int reload)
07648 {
07649    struct ast_config *cfg;
07650    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
07651    const char *cat = NULL;
07652    int res = 0;
07653    const char *val;
07654 
07655    if (!reload) {
07656       ast_mutex_init(&sla.lock);
07657       ast_cond_init(&sla.cond, NULL);
07658       sla_trunks = ao2_container_alloc(1, sla_trunk_hash, sla_trunk_cmp);
07659       sla_stations = ao2_container_alloc(1, sla_station_hash, sla_station_cmp);
07660    }
07661 
07662    if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags))) {
07663       return 0; /* Treat no config as normal */
07664    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
07665       return 0;
07666    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
07667       ast_log(LOG_ERROR, "Config file " SLA_CONFIG_FILE " is in an invalid format.  Aborting.\n");
07668       return 0;
07669    }
07670 
07671    if (reload) {
07672       ao2_callback(sla_trunks, 0, sla_trunk_mark, NULL);
07673       ao2_callback(sla_stations, 0, sla_station_mark, NULL);
07674    }
07675 
07676    if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
07677       sla.attempt_callerid = ast_true(val);
07678 
07679    while ((cat = ast_category_browse(cfg, cat)) && !res) {
07680       const char *type;
07681       if (!strcasecmp(cat, "general"))
07682          continue;
07683       if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
07684          ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
07685             SLA_CONFIG_FILE);
07686          continue;
07687       }
07688       if (!strcasecmp(type, "trunk"))
07689          res = sla_build_trunk(cfg, cat);
07690       else if (!strcasecmp(type, "station"))
07691          res = sla_build_station(cfg, cat);
07692       else {
07693          ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
07694             SLA_CONFIG_FILE, type);
07695       }
07696    }
07697 
07698    ast_config_destroy(cfg);
07699 
07700    if (reload) {
07701       ao2_callback(sla_trunks, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_trunk_is_marked, NULL);
07702       ao2_callback(sla_stations, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_station_is_marked, NULL);
07703    }
07704 
07705    /* Start SLA event processing thread once SLA has been configured. */
07706    if (sla.thread == AST_PTHREADT_NULL && sla_in_use()) {
07707       ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
07708    }
07709 
07710    return res;
07711 }
07712 
07713 static int acf_meetme_info_eval(const char *keyword, const struct ast_conference *conf)
07714 {
07715    if (!strcasecmp("lock", keyword)) {
07716       return conf->locked;
07717    } else if (!strcasecmp("parties", keyword)) {
07718       return conf->users;
07719    } else if (!strcasecmp("activity", keyword)) {
07720       time_t now;
07721       now = time(NULL);
07722       return (now - conf->start);
07723    } else if (!strcasecmp("dynamic", keyword)) {
07724       return conf->isdynamic;
07725    } else {
07726       return -1;
07727    }
07728 
07729 }
07730 
07731 static int acf_meetme_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
07732 {
07733    struct ast_conference *conf;
07734    char *parse;
07735    int result = -2; /* only non-negative numbers valid, -1 is used elsewhere */
07736    AST_DECLARE_APP_ARGS(args,
07737       AST_APP_ARG(keyword);
07738       AST_APP_ARG(confno);
07739    );
07740 
07741    if (ast_strlen_zero(data)) {
07742       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires two arguments\n");
07743       return -1;
07744    }
07745 
07746    parse = ast_strdupa(data);
07747    AST_STANDARD_APP_ARGS(args, parse);
07748 
07749    if (ast_strlen_zero(args.keyword)) {
07750       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a keyword\n");
07751       return -1;
07752    }
07753 
07754    if (ast_strlen_zero(args.confno)) {
07755       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a conference number\n");
07756       return -1;
07757    }
07758 
07759    AST_LIST_LOCK(&confs);
07760    AST_LIST_TRAVERSE(&confs, conf, list) {
07761       if (!strcmp(args.confno, conf->confno)) {
07762          result = acf_meetme_info_eval(args.keyword, conf);
07763          break;
07764       }
07765    }
07766    AST_LIST_UNLOCK(&confs);
07767 
07768    if (result > -1) {
07769       snprintf(buf, len, "%d", result);
07770    } else if (result == -1) {
07771       ast_log(LOG_NOTICE, "Error: invalid keyword: '%s'\n", args.keyword);
07772       snprintf(buf, len, "0");
07773    } else if (result == -2) {
07774       ast_log(LOG_NOTICE, "Error: conference (%s) not found\n", args.confno); 
07775       snprintf(buf, len, "0");
07776    }
07777 
07778    return 0;
07779 }
07780 
07781 
07782 static struct ast_custom_function meetme_info_acf = {
07783    .name = "MEETME_INFO",
07784    .read = acf_meetme_info,
07785 };
07786 
07787 
07788 static int load_config(int reload)
07789 {
07790    load_config_meetme();
07791    return sla_load_config(reload);
07792 }
07793 
07794 #define MEETME_DATA_EXPORT(MEMBER)              \
07795    MEMBER(ast_conference, confno, AST_DATA_STRING)       \
07796    MEMBER(ast_conference, dahdiconf, AST_DATA_INTEGER)      \
07797    MEMBER(ast_conference, users, AST_DATA_INTEGER)       \
07798    MEMBER(ast_conference, markedusers, AST_DATA_INTEGER)    \
07799    MEMBER(ast_conference, maxusers, AST_DATA_INTEGER)    \
07800    MEMBER(ast_conference, isdynamic, AST_DATA_BOOLEAN)      \
07801    MEMBER(ast_conference, locked, AST_DATA_BOOLEAN)      \
07802    MEMBER(ast_conference, recordingfilename, AST_DATA_STRING)  \
07803    MEMBER(ast_conference, recordingformat, AST_DATA_STRING) \
07804    MEMBER(ast_conference, pin, AST_DATA_PASSWORD)        \
07805    MEMBER(ast_conference, pinadmin, AST_DATA_PASSWORD)      \
07806    MEMBER(ast_conference, start, AST_DATA_TIMESTAMP)     \
07807    MEMBER(ast_conference, endtime, AST_DATA_TIMESTAMP)
07808 
07809 AST_DATA_STRUCTURE(ast_conference, MEETME_DATA_EXPORT);
07810 
07811 #define MEETME_USER_DATA_EXPORT(MEMBER)               \
07812    MEMBER(ast_conf_user, user_no, AST_DATA_INTEGER)      \
07813    MEMBER(ast_conf_user, talking, AST_DATA_BOOLEAN)      \
07814    MEMBER(ast_conf_user, dahdichannel, AST_DATA_BOOLEAN)    \
07815    MEMBER(ast_conf_user, jointime, AST_DATA_TIMESTAMP)      \
07816    MEMBER(ast_conf_user, kicktime, AST_DATA_TIMESTAMP)      \
07817    MEMBER(ast_conf_user, timelimit, AST_DATA_MILLISECONDS)     \
07818    MEMBER(ast_conf_user, play_warning, AST_DATA_MILLISECONDS)  \
07819    MEMBER(ast_conf_user, warning_freq, AST_DATA_MILLISECONDS)
07820 
07821 AST_DATA_STRUCTURE(ast_conf_user, MEETME_USER_DATA_EXPORT);
07822 
07823 static int user_add_provider_cb(void *obj, void *arg, int flags)
07824 {
07825    struct ast_data *data_meetme_user;
07826    struct ast_data *data_meetme_user_channel;
07827    struct ast_data *data_meetme_user_volume;
07828 
07829    struct ast_conf_user *user = obj;
07830    struct ast_data *data_meetme_users = arg;
07831 
07832    data_meetme_user = ast_data_add_node(data_meetme_users, "user");
07833    if (!data_meetme_user) {
07834       return 0;
07835    }
07836    /* user structure */
07837    ast_data_add_structure(ast_conf_user, data_meetme_user, user);
07838 
07839    /* user's channel */
07840    data_meetme_user_channel = ast_data_add_node(data_meetme_user, "channel");
07841    if (!data_meetme_user_channel) {
07842       return 0;
07843    }
07844 
07845    ast_channel_data_add_structure(data_meetme_user_channel, user->chan, 1);
07846 
07847    /* volume structure */
07848    data_meetme_user_volume = ast_data_add_node(data_meetme_user, "listen-volume");
07849    if (!data_meetme_user_volume) {
07850       return 0;
07851    }
07852    ast_data_add_int(data_meetme_user_volume, "desired", user->listen.desired);
07853    ast_data_add_int(data_meetme_user_volume, "actual", user->listen.actual);
07854 
07855    data_meetme_user_volume = ast_data_add_node(data_meetme_user, "talk-volume");
07856    if (!data_meetme_user_volume) {
07857       return 0;
07858    }
07859    ast_data_add_int(data_meetme_user_volume, "desired", user->talk.desired);
07860    ast_data_add_int(data_meetme_user_volume, "actual", user->talk.actual);
07861 
07862    return 0;
07863 }
07864 
07865 /*!
07866  * \internal
07867  * \brief Implements the meetme data provider.
07868  */
07869 static int meetme_data_provider_get(const struct ast_data_search *search,
07870    struct ast_data *data_root)
07871 {
07872    struct ast_conference *cnf;
07873    struct ast_data *data_meetme, *data_meetme_users;
07874 
07875    AST_LIST_LOCK(&confs);
07876    AST_LIST_TRAVERSE(&confs, cnf, list) {
07877       data_meetme = ast_data_add_node(data_root, "meetme");
07878       if (!data_meetme) {
07879          continue;
07880       }
07881 
07882       ast_data_add_structure(ast_conference, data_meetme, cnf);
07883 
07884       if (ao2_container_count(cnf->usercontainer)) {
07885          data_meetme_users = ast_data_add_node(data_meetme, "users");
07886          if (!data_meetme_users) {
07887             ast_data_remove_node(data_root, data_meetme);
07888             continue;
07889          }
07890 
07891          ao2_callback(cnf->usercontainer, OBJ_NODATA, user_add_provider_cb, data_meetme_users); 
07892       }
07893 
07894       if (!ast_data_search_match(search, data_meetme)) {
07895          ast_data_remove_node(data_root, data_meetme);
07896       }
07897    }
07898    AST_LIST_UNLOCK(&confs);
07899 
07900    return 0;
07901 }
07902 
07903 static const struct ast_data_handler meetme_data_provider = {
07904    .version = AST_DATA_HANDLER_VERSION,
07905    .get = meetme_data_provider_get
07906 };
07907 
07908 static const struct ast_data_entry meetme_data_providers[] = {
07909    AST_DATA_ENTRY("asterisk/application/meetme/list", &meetme_data_provider),
07910 };
07911 
07912 #ifdef TEST_FRAMEWORK
07913 AST_TEST_DEFINE(test_meetme_data_provider)
07914 {
07915    struct ast_channel *chan;
07916    struct ast_conference *cnf;
07917    struct ast_data *node;
07918    struct ast_data_query query = {
07919       .path = "/asterisk/application/meetme/list",
07920       .search = "list/meetme/confno=9898"
07921    };
07922 
07923    switch (cmd) {
07924    case TEST_INIT:
07925       info->name = "meetme_get_data_test";
07926       info->category = "/main/data/app_meetme/list/";
07927       info->summary = "Meetme data provider unit test";
07928       info->description =
07929          "Tests whether the Meetme data provider implementation works as expected.";
07930       return AST_TEST_NOT_RUN;
07931    case TEST_EXECUTE:
07932       break;
07933    }
07934 
07935    chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, 0, 0, "MeetMeTest");
07936    if (!chan) {
07937       ast_test_status_update(test, "Channel allocation failed\n");
07938       return AST_TEST_FAIL;
07939    }
07940 
07941    cnf = build_conf("9898", "", "1234", 1, 1, 1, chan, test);
07942    if (!cnf) {
07943       ast_test_status_update(test, "Build of test conference 9898 failed\n");
07944       ast_hangup(chan);
07945       return AST_TEST_FAIL;
07946    }
07947 
07948    node = ast_data_get(&query);
07949    if (!node) {
07950       ast_test_status_update(test, "Data query for test conference 9898 failed\n");
07951       dispose_conf(cnf);
07952       ast_hangup(chan);
07953       return AST_TEST_FAIL;
07954    }
07955 
07956    if (strcmp(ast_data_retrieve_string(node, "meetme/confno"), "9898")) {
07957       ast_test_status_update(test, "Query returned the wrong conference\n");
07958       dispose_conf(cnf);
07959       ast_hangup(chan);
07960       ast_data_free(node);
07961       return AST_TEST_FAIL;
07962    }
07963 
07964    ast_data_free(node);
07965    dispose_conf(cnf);
07966    ast_hangup(chan);
07967 
07968    return AST_TEST_PASS;
07969 }
07970 #endif
07971 
07972 static int unload_module(void)
07973 {
07974    int res = 0;
07975    
07976    ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
07977    res = ast_manager_unregister("MeetmeMute");
07978    res |= ast_manager_unregister("MeetmeUnmute");
07979    res |= ast_manager_unregister("MeetmeList");
07980    res |= ast_manager_unregister("MeetmeListRooms");
07981    res |= ast_unregister_application(app4);
07982    res |= ast_unregister_application(app3);
07983    res |= ast_unregister_application(app2);
07984    res |= ast_unregister_application(app);
07985    res |= ast_unregister_application(slastation_app);
07986    res |= ast_unregister_application(slatrunk_app);
07987 
07988 #ifdef TEST_FRAMEWORK
07989    AST_TEST_UNREGISTER(test_meetme_data_provider);
07990 #endif
07991    ast_data_unregister(NULL);
07992 
07993    ast_devstate_prov_del("Meetme");
07994    ast_devstate_prov_del("SLA");
07995    
07996    sla_destroy();
07997    
07998    res |= ast_custom_function_unregister(&meetme_info_acf);
07999    ast_unload_realtime("meetme");
08000 
08001    return res;
08002 }
08003 
08004 static int load_module(void)
08005 {
08006    int res = 0;
08007 
08008    res |= load_config(0);
08009 
08010    ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
08011    res |= ast_manager_register_xml("MeetmeMute", EVENT_FLAG_CALL, action_meetmemute);
08012    res |= ast_manager_register_xml("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute);
08013    res |= ast_manager_register_xml("MeetmeList", EVENT_FLAG_REPORTING, action_meetmelist);
08014    res |= ast_manager_register_xml("MeetmeListRooms", EVENT_FLAG_REPORTING, action_meetmelistrooms);
08015    res |= ast_register_application_xml(app4, channel_admin_exec);
08016    res |= ast_register_application_xml(app3, admin_exec);
08017    res |= ast_register_application_xml(app2, count_exec);
08018    res |= ast_register_application_xml(app, conf_exec);
08019    res |= ast_register_application_xml(slastation_app, sla_station_exec);
08020    res |= ast_register_application_xml(slatrunk_app, sla_trunk_exec);
08021 
08022 #ifdef TEST_FRAMEWORK
08023    AST_TEST_REGISTER(test_meetme_data_provider);
08024 #endif
08025    ast_data_register_multiple(meetme_data_providers, ARRAY_LEN(meetme_data_providers));
08026 
08027    res |= ast_devstate_prov_add("Meetme", meetmestate);
08028    res |= ast_devstate_prov_add("SLA", sla_state);
08029 
08030    res |= ast_custom_function_register(&meetme_info_acf);
08031    ast_realtime_require_field("meetme", "confno", RQ_UINTEGER2, 3, "members", RQ_UINTEGER1, 3, NULL);
08032 
08033    return res;
08034 }
08035 
08036 static int reload(void)
08037 {
08038    ast_unload_realtime("meetme");
08039    return load_config(1);
08040 }
08041 
08042 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "MeetMe conference bridge",
08043       .load = load_module,
08044       .unload = unload_module,
08045       .reload = reload,
08046       .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
08047           );
08048