Sat Jul 12 2014 17:18:24

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: 414402 $")
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  */
02633 static void meetme_menu_admin_extended(enum menu_modes *menu_mode, int *dtmf,
02634    struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan,
02635    struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size,
02636    struct ast_format_cap *cap_slin)
02637 {
02638    int keepplaying;
02639    int playednamerec;
02640    int res;
02641    struct ao2_iterator user_iter;
02642    struct ast_conf_user *usr = NULL;
02643 
02644    switch(*dtmf) {
02645    case '1': /* *81 Roll call */
02646       keepplaying = 1;
02647       playednamerec = 0;
02648       if (conf->users == 1) {
02649          if (keepplaying && !ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
02650             res = ast_waitstream(chan, AST_DIGIT_ANY);
02651             ast_stopstream(chan);
02652             if (res > 0) {
02653                keepplaying = 0;
02654             }
02655          }
02656       } else if (conf->users == 2) {
02657          if (keepplaying && !ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
02658             res = ast_waitstream(chan, AST_DIGIT_ANY);
02659             ast_stopstream(chan);
02660             if (res > 0) {
02661                keepplaying = 0;
02662             }
02663          }
02664       } else {
02665          if (keepplaying && !ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
02666             res = ast_waitstream(chan, AST_DIGIT_ANY);
02667             ast_stopstream(chan);
02668             if (res > 0) {
02669                keepplaying = 0;
02670             }
02671          }
02672          if (keepplaying) {
02673             res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
02674             ast_stopstream(chan);
02675             if (res > 0) {
02676                keepplaying = 0;
02677             }
02678          }
02679          if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
02680             res = ast_waitstream(chan, AST_DIGIT_ANY);
02681             ast_stopstream(chan);
02682             if (res > 0) {
02683                keepplaying = 0;
02684             }
02685          }
02686       }
02687       user_iter = ao2_iterator_init(conf->usercontainer, 0);
02688       while((usr = ao2_iterator_next(&user_iter))) {
02689          if (ast_fileexists(usr->namerecloc, NULL, NULL)) {
02690             if (keepplaying && !ast_streamfile(chan, usr->namerecloc, ast_channel_language(chan))) {
02691                res = ast_waitstream(chan, AST_DIGIT_ANY);
02692                ast_stopstream(chan);
02693                if (res > 0) {
02694                   keepplaying = 0;
02695                }
02696             }
02697             playednamerec = 1;
02698          }
02699          ao2_ref(usr, -1);
02700       }
02701       ao2_iterator_destroy(&user_iter);
02702       if (keepplaying && playednamerec && !ast_streamfile(chan, "conf-roll-callcomplete", ast_channel_language(chan))) {
02703          res = ast_waitstream(chan, AST_DIGIT_ANY);
02704          ast_stopstream(chan);
02705          if (res > 0) {
02706             keepplaying = 0;
02707          }
02708       }
02709 
02710       *menu_mode = MENU_DISABLED;
02711       break;
02712 
02713    case '2': /* *82 Eject all non-admins */
02714       if (conf->users == 1) {
02715          if(!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02716             ast_waitstream(chan, "");
02717          }
02718       } else {
02719          ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_kickme_cb, &conf);
02720       }
02721       ast_stopstream(chan);
02722       *menu_mode = MENU_DISABLED;
02723       break;
02724 
02725    case '3': /* *83 (Admin) mute/unmute all non-admins */
02726       if(conf->gmuted) {
02727          conf->gmuted = 0;
02728          ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, &conf);
02729          if (!ast_streamfile(chan, "conf-now-unmuted", ast_channel_language(chan))) {
02730             ast_waitstream(chan, "");
02731          }
02732       } else {
02733          conf->gmuted = 1;
02734          ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_muted_cb, &conf);
02735          if (!ast_streamfile(chan, "conf-now-muted", ast_channel_language(chan))) {
02736             ast_waitstream(chan, "");
02737          }
02738       }
02739       ast_stopstream(chan);
02740       *menu_mode = MENU_DISABLED;
02741       break;
02742 
02743    case '4': /* *84 Record conference */
02744       if (conf->recording != MEETME_RECORD_ACTIVE) {
02745          ast_set_flag64(confflags, CONFFLAG_RECORDCONF);
02746          if (!conf->recordingfilename) {
02747             const char *var;
02748             ast_channel_lock(chan);
02749             if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
02750                conf->recordingfilename = ast_strdup(var);
02751             }
02752             if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
02753                conf->recordingformat = ast_strdup(var);
02754             }
02755             ast_channel_unlock(chan);
02756             if (!conf->recordingfilename) {
02757                snprintf(recordingtmp, recordingtmp_size, "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
02758                conf->recordingfilename = ast_strdup(recordingtmp);
02759             }
02760             if (!conf->recordingformat) {
02761                conf->recordingformat = ast_strdup("wav");
02762             }
02763             ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
02764             conf->confno, conf->recordingfilename, conf->recordingformat);
02765          }
02766 
02767          ast_mutex_lock(&conf->recordthreadlock);
02768          if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) {
02769             struct dahdi_confinfo dahdic;
02770 
02771             ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
02772             ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
02773             dahdic.chan = 0;
02774             dahdic.confno = conf->dahdiconf;
02775             dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
02776             if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) {
02777                ast_log(LOG_WARNING, "Error starting listen channel\n");
02778                ast_hangup(conf->lchan);
02779                conf->lchan = NULL;
02780             } else {
02781                ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
02782             }
02783          }
02784          ast_mutex_unlock(&conf->recordthreadlock);
02785          if (!ast_streamfile(chan, "conf-now-recording", ast_channel_language(chan))) {
02786             ast_waitstream(chan, "");
02787          }
02788       }
02789 
02790       ast_stopstream(chan);
02791       *menu_mode = MENU_DISABLED;
02792       break;
02793 
02794    case '8': /* *88 Exit the menu and return to the conference... without an error message */
02795       ast_stopstream(chan);
02796       *menu_mode = MENU_DISABLED;
02797       break;
02798 
02799    default:
02800       if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02801          ast_waitstream(chan, "");
02802       }
02803       ast_stopstream(chan);
02804       *menu_mode = MENU_DISABLED;
02805       break;
02806    }
02807 }
02808 
02809 /*! \internal
02810  * \brief Processes menu options for the various menu types (accessible through the 's' option for app_meetme)
02811  *
02812  * \param menu_mode a pointer to the currently active menu_mode.
02813  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
02814  * \param conf the active conference for which the user has called the menu from.
02815  * \param confflags flags used by conf for various options
02816  * \param chan ast_channel belonging to the user who called the menu
02817  * \param user which meetme conference user invoked the menu
02818  * \param recordingtmp character buffer which may hold the name of the conference recording file
02819  */
02820 static void meetme_menu(enum menu_modes *menu_mode, int *dtmf,
02821    struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan,
02822    struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size,
02823    struct ast_format_cap *cap_slin)
02824 {
02825    switch (*menu_mode) {
02826    case MENU_DISABLED:
02827       break;
02828    case MENU_NORMAL:
02829       meetme_menu_normal(menu_mode, dtmf, conf, confflags, chan, user);
02830       break;
02831    case MENU_ADMIN:
02832       meetme_menu_admin(menu_mode, dtmf, conf, confflags, chan, user);
02833       /* Admin Menu is capable of branching into another menu, in which case it will reset dtmf and change the menu mode. */
02834       if (*menu_mode != MENU_ADMIN_EXTENDED || (*dtmf <= 0)) {
02835          break;
02836       }
02837    case MENU_ADMIN_EXTENDED:
02838       meetme_menu_admin_extended(menu_mode, dtmf, conf, confflags, chan, user,
02839          recordingtmp, recordingtmp_size, cap_slin);
02840       break;
02841    }
02842 }
02843 
02844 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
02845 {
02846    struct ast_conf_user *user = NULL;
02847    int fd;
02848    struct dahdi_confinfo dahdic, dahdic_empty;
02849    struct ast_frame *f;
02850    struct ast_channel *c;
02851    struct ast_frame fr;
02852    int outfd;
02853    int ms;
02854    int nfds;
02855    int res;
02856    int retrydahdi;
02857    int origfd;
02858    int musiconhold = 0, mohtempstopped = 0;
02859    int firstpass = 0;
02860    int lastmarked = 0;
02861    int currentmarked = 0;
02862    int ret = -1;
02863    int x;
02864    enum menu_modes menu_mode = MENU_DISABLED;
02865    int talkreq_manager = 0;
02866    int using_pseudo = 0;
02867    int duration = 20;
02868    int sent_event = 0;
02869    int checked = 0;
02870    int announcement_played = 0;
02871    struct timeval now;
02872    struct ast_dsp *dsp = NULL;
02873    struct ast_app *agi_app;
02874    char *agifile, *mod_speex;
02875    const char *agifiledefault = "conf-background.agi", *tmpvar;
02876    char meetmesecs[30] = "";
02877    char exitcontext[AST_MAX_CONTEXT] = "";
02878    char recordingtmp[AST_MAX_EXTENSION] = "";
02879    char members[10] = "";
02880    int dtmf = 0, opt_waitmarked_timeout = 0;
02881    time_t timeout = 0;
02882    struct dahdi_bufferinfo bi;
02883    char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
02884    char *buf = __buf + AST_FRIENDLY_OFFSET;
02885    char *exitkeys = NULL;
02886    unsigned int calldurationlimit = 0;
02887    long timelimit = 0;
02888    long play_warning = 0;
02889    long warning_freq = 0;
02890    const char *warning_sound = NULL;
02891    const char *end_sound = NULL;
02892    char *parse;
02893    long time_left_ms = 0;
02894    struct timeval nexteventts = { 0, };
02895    int to;
02896    int setusercount = 0;
02897    int confsilence = 0, totalsilence = 0;
02898    char *mailbox, *context;
02899    struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
02900    struct ast_format tmpfmt;
02901 
02902    if (!cap_slin) {
02903       goto conf_run_cleanup;
02904    }
02905    ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
02906 
02907    if (!(user = ao2_alloc(sizeof(*user), NULL))) {
02908       goto conf_run_cleanup;
02909    }
02910 
02911    /* Possible timeout waiting for marked user */
02912    if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
02913       !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
02914       (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
02915       (opt_waitmarked_timeout > 0)) {
02916       timeout = time(NULL) + opt_waitmarked_timeout;
02917    }
02918 
02919    if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
02920       calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
02921       ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
02922    }
02923 
02924    if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
02925       char *limit_str, *warning_str, *warnfreq_str;
02926       const char *var;
02927 
02928       parse = optargs[OPT_ARG_DURATION_LIMIT];
02929       limit_str = strsep(&parse, ":");
02930       warning_str = strsep(&parse, ":");
02931       warnfreq_str = parse;
02932 
02933       timelimit = atol(limit_str);
02934       if (warning_str)
02935          play_warning = atol(warning_str);
02936       if (warnfreq_str)
02937          warning_freq = atol(warnfreq_str);
02938 
02939       if (!timelimit) {
02940          timelimit = play_warning = warning_freq = 0;
02941          warning_sound = NULL;
02942       } else if (play_warning > timelimit) {
02943          if (!warning_freq) {
02944             play_warning = 0;
02945          } else {
02946             while (play_warning > timelimit)
02947                play_warning -= warning_freq;
02948             if (play_warning < 1)
02949                play_warning = warning_freq = 0;
02950          }
02951       }
02952 
02953       ast_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
02954       if (play_warning) {
02955          ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
02956       }
02957       if (warning_freq) {
02958          ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
02959       }
02960 
02961       ast_channel_lock(chan);
02962       if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
02963          var = ast_strdupa(var);
02964       }
02965       ast_channel_unlock(chan);
02966 
02967       warning_sound = var ? var : "timeleft";
02968 
02969       ast_channel_lock(chan);
02970       if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
02971          var = ast_strdupa(var);
02972       }
02973       ast_channel_unlock(chan);
02974 
02975       end_sound = var ? var : NULL;
02976 
02977       /* undo effect of S(x) in case they are both used */
02978       calldurationlimit = 0;
02979       /* more efficient do it like S(x) does since no advanced opts */
02980       if (!play_warning && !end_sound && timelimit) {
02981          calldurationlimit = timelimit / 1000;
02982          timelimit = play_warning = warning_freq = 0;
02983       } else {
02984          ast_debug(2, "Limit Data for this call:\n");
02985          ast_debug(2, "- timelimit     = %ld\n", timelimit);
02986          ast_debug(2, "- play_warning  = %ld\n", play_warning);
02987          ast_debug(2, "- warning_freq  = %ld\n", warning_freq);
02988          ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
02989          ast_debug(2, "- end_sound     = %s\n", end_sound ? end_sound : "UNDEF");
02990       }
02991    }
02992 
02993    /* Get exit keys */
02994    if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
02995       if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
02996          exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
02997       else
02998          exitkeys = ast_strdupa("#"); /* Default */
02999    }
03000    
03001    if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
03002       if (!conf->recordingfilename) {
03003          const char *var;
03004          ast_channel_lock(chan);
03005          if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
03006             conf->recordingfilename = ast_strdup(var);
03007          }
03008          if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
03009             conf->recordingformat = ast_strdup(var);
03010          }
03011          ast_channel_unlock(chan);
03012          if (!conf->recordingfilename) {
03013             snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
03014             conf->recordingfilename = ast_strdup(recordingtmp);
03015          }
03016          if (!conf->recordingformat) {
03017             conf->recordingformat = ast_strdup("wav");
03018          }
03019          ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
03020                 conf->confno, conf->recordingfilename, conf->recordingformat);
03021       }
03022    }
03023 
03024    ast_mutex_lock(&conf->recordthreadlock);
03025    if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
03026       ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) {
03027       ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
03028       ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
03029       dahdic.chan = 0;
03030       dahdic.confno = conf->dahdiconf;
03031       dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
03032       if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) {
03033          ast_log(LOG_WARNING, "Error starting listen channel\n");
03034          ast_hangup(conf->lchan);
03035          conf->lchan = NULL;
03036       } else {
03037          ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
03038       }
03039    }
03040    ast_mutex_unlock(&conf->recordthreadlock);
03041 
03042    ast_mutex_lock(&conf->announcethreadlock);
03043    if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03044       ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) {
03045       ast_mutex_init(&conf->announcelistlock);
03046       AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
03047       ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
03048    }
03049    ast_mutex_unlock(&conf->announcethreadlock);
03050 
03051    time(&user->jointime);
03052    
03053    user->timelimit = timelimit;
03054    user->play_warning = play_warning;
03055    user->warning_freq = warning_freq;
03056    user->warning_sound = warning_sound;
03057    user->end_sound = end_sound;  
03058    
03059    if (calldurationlimit > 0) {
03060       time(&user->kicktime);
03061       user->kicktime = user->kicktime + calldurationlimit;
03062    }
03063    
03064    if (ast_tvzero(user->start_time))
03065       user->start_time = ast_tvnow();
03066    time_left_ms = user->timelimit;
03067    
03068    if (user->timelimit) {
03069       nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
03070       nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
03071    }
03072 
03073    if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
03074       /* Sorry, but this conference is locked! */  
03075       if (!ast_streamfile(chan, "conf-locked", ast_channel_language(chan)))
03076          ast_waitstream(chan, "");
03077       goto outrun;
03078    }
03079 
03080       ast_mutex_lock(&conf->playlock);
03081 
03082    if (rt_schedule && conf->maxusers) {
03083       if (conf->users >= conf->maxusers) {
03084          /* Sorry, but this confernce has reached the participant limit! */   
03085          ast_mutex_unlock(&conf->playlock);
03086          if (!ast_streamfile(chan, "conf-full", ast_channel_language(chan)))
03087             ast_waitstream(chan, "");
03088          goto outrun;
03089       }
03090    }
03091 
03092    ao2_lock(conf->usercontainer);
03093    ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
03094    user->user_no++;
03095    ao2_link(conf->usercontainer, user);
03096    ao2_unlock(conf->usercontainer);
03097 
03098    user->chan = chan;
03099    user->userflags = *confflags;
03100    user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
03101    user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
03102    user->talking = -1;
03103 
03104    ast_mutex_unlock(&conf->playlock);
03105 
03106    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC))) {
03107       char destdir[PATH_MAX];
03108 
03109       snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
03110 
03111       if (ast_mkdir(destdir, 0777) != 0) {
03112          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
03113          goto outrun;
03114       }
03115 
03116       if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
03117          context = ast_strdupa(optargs[OPT_ARG_INTROUSER_VMREC]);
03118          mailbox = strsep(&context, "@");
03119 
03120          if (ast_strlen_zero(mailbox)) {
03121             /* invalid input, clear the v flag*/
03122             ast_clear_flag64(confflags,CONFFLAG_INTROUSER_VMREC);
03123             ast_log(LOG_WARNING,"You must specify a mailbox in the v() option\n");
03124          } else {
03125             if (ast_strlen_zero(context)) {
03126                 context = "default";
03127             }
03128             /* if there is no mailbox we don't need to do this logic  */
03129             snprintf(user->namerecloc, sizeof(user->namerecloc),
03130                 "%s/voicemail/%s/%s/greet",ast_config_AST_SPOOL_DIR,context,mailbox);
03131 
03132             /* if the greeting doesn't exist then use the temp file method instead, clear flag v */
03133             if (!ast_fileexists(user->namerecloc, NULL, NULL)){
03134                snprintf(user->namerecloc, sizeof(user->namerecloc),
03135                    "%s/meetme-username-%s-%d", destdir,
03136                    conf->confno, user->user_no);
03137                ast_clear_flag64(confflags, CONFFLAG_INTROUSER_VMREC);
03138             }
03139          }
03140       } else {
03141          snprintf(user->namerecloc, sizeof(user->namerecloc),
03142              "%s/meetme-username-%s-%d", destdir,
03143              conf->confno, user->user_no);
03144       }
03145 
03146       res = 0;
03147       if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) && !ast_fileexists(user->namerecloc, NULL, NULL))
03148          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);
03149       else if (ast_test_flag64(confflags, CONFFLAG_INTROUSER) && !ast_fileexists(user->namerecloc, NULL, NULL))
03150          res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
03151       if (res == -1)
03152          goto outrun;
03153 
03154    }
03155 
03156    ast_mutex_lock(&conf->playlock);
03157 
03158    if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
03159       conf->markedusers++;
03160    conf->users++;
03161    if (rt_log_members) {
03162       /* Update table */
03163       snprintf(members, sizeof(members), "%d", conf->users);
03164       ast_realtime_require_field("meetme",
03165          "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
03166          "members", RQ_UINTEGER1, strlen(members),
03167          NULL);
03168       ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
03169    }
03170    setusercount = 1;
03171 
03172    /* This device changed state now - if this is the first user */
03173    if (conf->users == 1)
03174       ast_devstate_changed(AST_DEVICE_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
03175 
03176    ast_mutex_unlock(&conf->playlock);
03177 
03178    /* return the unique ID of the conference */
03179    pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
03180 
03181    if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
03182       ast_channel_lock(chan);
03183       if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
03184          ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
03185       } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))) {
03186          ast_copy_string(exitcontext, ast_channel_macrocontext(chan), sizeof(exitcontext));
03187       } else {
03188          ast_copy_string(exitcontext, ast_channel_context(chan), sizeof(exitcontext));
03189       }
03190       ast_channel_unlock(chan);
03191    }
03192 
03193    /* Play an arbitrary intro message */
03194    if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
03195          !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
03196       if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], ast_channel_language(chan))) {
03197          ast_waitstream(chan, "");
03198       }
03199    }
03200 
03201    if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
03202       if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
03203          if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan)))
03204             ast_waitstream(chan, "");
03205       if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
03206          if (!ast_streamfile(chan, "conf-waitforleader", ast_channel_language(chan)))
03207             ast_waitstream(chan, "");
03208    }
03209 
03210    if (ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
03211       int keepplaying = 1;
03212 
03213       if (conf->users == 2) { 
03214          if (!ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
03215             res = ast_waitstream(chan, AST_DIGIT_ANY);
03216             ast_stopstream(chan);
03217             if (res > 0)
03218                keepplaying = 0;
03219             else if (res == -1)
03220                goto outrun;
03221          }
03222       } else { 
03223          if (!ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
03224             res = ast_waitstream(chan, AST_DIGIT_ANY);
03225             ast_stopstream(chan);
03226             if (res > 0)
03227                keepplaying = 0;
03228             else if (res == -1)
03229                goto outrun;
03230          }
03231          if (keepplaying) {
03232             res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03233             if (res > 0)
03234                keepplaying = 0;
03235             else if (res == -1)
03236                goto outrun;
03237          }
03238          if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
03239             res = ast_waitstream(chan, AST_DIGIT_ANY);
03240             ast_stopstream(chan);
03241             if (res > 0)
03242                keepplaying = 0;
03243             else if (res == -1) 
03244                goto outrun;
03245          }
03246       }
03247    }
03248 
03249    if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
03250       /* We're leaving this alone until the state gets changed to up */
03251       ast_indicate(chan, -1);
03252    }
03253 
03254    if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
03255       ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", ast_channel_name(chan));
03256       goto outrun;
03257    }
03258 
03259    if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
03260       ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", ast_channel_name(chan));
03261       goto outrun;
03262    }
03263 
03264    /* Reduce background noise from each participant */
03265    if (!ast_test_flag64(confflags, CONFFLAG_DONT_DENOISE) &&
03266       (mod_speex = ast_module_helper("", "func_speex", 0, 0, 0, 0))) {
03267       ast_free(mod_speex);
03268       ast_func_write(chan, "DENOISE(rx)", "on");
03269    }
03270 
03271    retrydahdi = (strcasecmp(ast_channel_tech(chan)->type, "DAHDI") || (ast_channel_audiohooks(chan) || ast_channel_monitor(chan)) ? 1 : 0);
03272    user->dahdichannel = !retrydahdi;
03273 
03274  dahdiretry:
03275    origfd = ast_channel_fd(chan, 0);
03276    if (retrydahdi) {
03277       /* open pseudo in non-blocking mode */
03278       fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
03279       if (fd < 0) {
03280          ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
03281          goto outrun;
03282       }
03283       using_pseudo = 1;
03284       /* Setup buffering information */
03285       memset(&bi, 0, sizeof(bi));
03286       bi.bufsize = CONF_SIZE / 2;
03287       bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
03288       bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
03289       bi.numbufs = audio_buffers;
03290       if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
03291          ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
03292          close(fd);
03293          goto outrun;
03294       }
03295       x = 1;
03296       if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
03297          ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
03298          close(fd);
03299          goto outrun;
03300       }
03301       nfds = 1;
03302    } else {
03303       /* XXX Make sure we're not running on a pseudo channel XXX */
03304       fd = ast_channel_fd(chan, 0);
03305       nfds = 0;
03306    }
03307    memset(&dahdic, 0, sizeof(dahdic));
03308    memset(&dahdic_empty, 0, sizeof(dahdic_empty));
03309    /* Check to see if we're in a conference... */
03310    dahdic.chan = 0;
03311    if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
03312       ast_log(LOG_WARNING, "Error getting conference\n");
03313       close(fd);
03314       goto outrun;
03315    }
03316    if (dahdic.confmode) {
03317       /* Whoa, already in a conference...  Retry... */
03318       if (!retrydahdi) {
03319          ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
03320          retrydahdi = 1;
03321          goto dahdiretry;
03322       }
03323    }
03324    memset(&dahdic, 0, sizeof(dahdic));
03325    /* Add us to the conference */
03326    dahdic.chan = 0;
03327    dahdic.confno = conf->dahdiconf;
03328 
03329    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
03330          ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) || ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)) && conf->users > 1) {
03331       struct announce_listitem *item;
03332       if (!(item = ao2_alloc(sizeof(*item), NULL)))
03333          goto outrun;
03334       ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
03335       ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
03336       item->confchan = conf->chan;
03337       item->confusers = conf->users;
03338       if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
03339          item->vmrec = 1;
03340       }
03341       item->announcetype = CONF_HASJOIN;
03342       ast_mutex_lock(&conf->announcelistlock);
03343       ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
03344       AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
03345       ast_cond_signal(&conf->announcelist_addition);
03346       ast_mutex_unlock(&conf->announcelistlock);
03347 
03348       while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
03349          ;
03350       }
03351       ao2_ref(item, -1);
03352    }
03353 
03354    if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && !conf->markedusers)
03355       dahdic.confmode = DAHDI_CONF_CONF;
03356    else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
03357       dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
03358    else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
03359       dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
03360    else
03361       dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
03362 
03363    if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03364       ast_log(LOG_WARNING, "Error setting conference\n");
03365       close(fd);
03366       goto outrun;
03367    }
03368    ast_debug(1, "Placed channel %s in DAHDI conf %d\n", ast_channel_name(chan), conf->dahdiconf);
03369 
03370    if (!sent_event) {
03371       /*** DOCUMENTATION
03372          <managerEventInstance>
03373             <synopsis>Raised when a user joins a MeetMe conference.</synopsis>
03374             <syntax>
03375                <parameter name="Meetme">
03376                   <para>The identifier for the MeetMe conference.</para>
03377                </parameter>
03378                <parameter name="Usernum">
03379                   <para>The identifier of the MeetMe user who joined.</para>
03380                </parameter>
03381             </syntax>
03382             <see-also>
03383                <ref type="managerEvent">MeetmeLeave</ref>
03384                <ref type="application">MeetMe</ref>
03385             </see-also>
03386          </managerEventInstance>
03387       ***/
03388       ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
03389          "Channel: %s\r\n"
03390          "Uniqueid: %s\r\n"
03391          "Meetme: %s\r\n"
03392          "Usernum: %d\r\n"
03393          "CallerIDnum: %s\r\n"
03394          "CallerIDname: %s\r\n"
03395          "ConnectedLineNum: %s\r\n"
03396          "ConnectedLineName: %s\r\n",
03397          ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno,
03398          user->user_no,
03399          S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
03400          S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<unknown>"),
03401          S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, "<unknown>"),
03402          S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "<unknown>")
03403          );
03404       sent_event = 1;
03405    }
03406 
03407    if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
03408       !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03409       firstpass = 1;
03410       if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
03411          if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
03412             (conf->markedusers >= 1))) {
03413             conf_play(chan, conf, ENTER);
03414          }
03415    }
03416 
03417    conf_flush(fd, chan);
03418 
03419    if (dsp)
03420       ast_dsp_free(dsp);
03421 
03422    if (!(dsp = ast_dsp_new())) {
03423       ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
03424       res = -1;
03425    }
03426 
03427    if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
03428       /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
03429          or use default filename of conf-background.agi */
03430 
03431       ast_channel_lock(chan);
03432       if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
03433          agifile = ast_strdupa(tmpvar);
03434       } else {
03435          agifile = ast_strdupa(agifiledefault);
03436       }
03437       ast_channel_unlock(chan);
03438       
03439       if (user->dahdichannel) {
03440          /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
03441          x = 1;
03442          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03443       }
03444       /* Find a pointer to the agi app and execute the script */
03445       agi_app = pbx_findapp("agi");
03446       if (agi_app) {
03447          ret = pbx_exec(chan, agi_app, agifile);
03448       } else {
03449          ast_log(LOG_WARNING, "Could not find application (agi)\n");
03450          ret = -2;
03451       }
03452       if (user->dahdichannel) {
03453          /*  Remove CONFMUTE mode on DAHDI channel */
03454          x = 0;
03455          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03456       }
03457    } else {
03458       int lastusers = conf->users;
03459       if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
03460          /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
03461          x = 1;
03462          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03463       }
03464 
03465       for (;;) {
03466          int menu_was_active = 0;
03467 
03468          outfd = -1;
03469          ms = -1;
03470          now = ast_tvnow();
03471 
03472          if (rt_schedule && conf->endtime) {
03473             char currenttime[32];
03474             long localendtime = 0;
03475             int extended = 0;
03476             struct ast_tm tm;
03477             struct ast_variable *var, *origvar;
03478             struct timeval tmp;
03479 
03480             if (now.tv_sec % 60 == 0) {
03481                if (!checked) {
03482                   ast_localtime(&now, &tm, NULL);
03483                   ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
03484                   var = origvar = ast_load_realtime("meetme", "confno",
03485                      conf->confno, "starttime <=", currenttime,
03486                       "endtime >=", currenttime, NULL);
03487 
03488                   for ( ; var; var = var->next) {
03489                      if (!strcasecmp(var->name, "endtime")) {
03490                         struct ast_tm endtime_tm;
03491                         ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
03492                         tmp = ast_mktime(&endtime_tm, NULL);
03493                         localendtime = tmp.tv_sec;
03494                      }
03495                   }
03496                   ast_variables_destroy(origvar);
03497 
03498                   /* A conference can be extended from the
03499                      Admin/User menu or by an external source */
03500                   if (localendtime > conf->endtime){
03501                      conf->endtime = localendtime;
03502                      extended = 1;
03503                   }
03504 
03505                   if (conf->endtime && (now.tv_sec >= conf->endtime)) {
03506                      ast_verbose("Quitting time...\n");
03507                      goto outrun;
03508                   }
03509 
03510                   if (!announcement_played && conf->endalert) {
03511                      if (now.tv_sec + conf->endalert >= conf->endtime) {
03512                         if (!ast_streamfile(chan, "conf-will-end-in", ast_channel_language(chan)))
03513                            ast_waitstream(chan, "");
03514                         ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", ast_channel_language(chan));
03515                         if (!ast_streamfile(chan, "minutes", ast_channel_language(chan)))
03516                            ast_waitstream(chan, "");
03517                         if (musiconhold) {
03518                            conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03519                         }
03520                         announcement_played = 1;
03521                      }
03522                   }
03523 
03524                   if (extended) {
03525                      announcement_played = 0;
03526                   }
03527 
03528                   checked = 1;
03529                }
03530             } else {
03531                checked = 0;
03532             }
03533          }
03534 
03535          if (user->kicktime && (user->kicktime <= now.tv_sec)) {
03536             if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03537                ret = 0;
03538             } else {
03539                ret = -1;
03540             }
03541             break;
03542          }
03543   
03544          to = -1;
03545          if (user->timelimit) {
03546             int minutes = 0, seconds = 0, remain = 0;
03547  
03548             to = ast_tvdiff_ms(nexteventts, now);
03549             if (to < 0) {
03550                to = 0;
03551             }
03552             time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
03553             if (time_left_ms < to) {
03554                to = time_left_ms;
03555             }
03556    
03557             if (time_left_ms <= 0) {
03558                if (user->end_sound) {                 
03559                   res = ast_streamfile(chan, user->end_sound, ast_channel_language(chan));
03560                   res = ast_waitstream(chan, "");
03561                }
03562                if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03563                   ret = 0;
03564                } else {
03565                   ret = -1;
03566                }
03567                break;
03568             }
03569             
03570             if (!to) {
03571                if (time_left_ms >= 5000) {                  
03572                   
03573                   remain = (time_left_ms + 500) / 1000;
03574                   if (remain / 60 >= 1) {
03575                      minutes = remain / 60;
03576                      seconds = remain % 60;
03577                   } else {
03578                      seconds = remain;
03579                   }
03580                   
03581                   /* force the time left to round up if appropriate */
03582                   if (user->warning_sound && user->play_warning) {
03583                      if (!strcmp(user->warning_sound, "timeleft")) {
03584                         
03585                         res = ast_streamfile(chan, "vm-youhave", ast_channel_language(chan));
03586                         res = ast_waitstream(chan, "");
03587                         if (minutes) {
03588                            res = ast_say_number(chan, minutes, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03589                            res = ast_streamfile(chan, "queue-minutes", ast_channel_language(chan));
03590                            res = ast_waitstream(chan, "");
03591                         }
03592                         if (seconds) {
03593                            res = ast_say_number(chan, seconds, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03594                            res = ast_streamfile(chan, "queue-seconds", ast_channel_language(chan));
03595                            res = ast_waitstream(chan, "");
03596                         }
03597                      } else {
03598                         res = ast_streamfile(chan, user->warning_sound, ast_channel_language(chan));
03599                         res = ast_waitstream(chan, "");
03600                      }
03601                      if (musiconhold) {
03602                         conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03603                      }
03604                   }
03605                }
03606                if (user->warning_freq) {
03607                   nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
03608                } else {
03609                   nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
03610                }
03611             }
03612          }
03613 
03614          now = ast_tvnow();
03615          if (timeout && now.tv_sec >= timeout) {
03616             if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03617                ret = 0;
03618             } else {
03619                ret = -1;
03620             }
03621             break;
03622          }
03623 
03624          /* if we have just exited from the menu, and the user had a channel-driver
03625             volume adjustment, restore it
03626          */
03627          if (!menu_mode && menu_was_active && user->listen.desired && !user->listen.actual) {
03628             set_talk_volume(user, user->listen.desired);
03629          }
03630 
03631          menu_was_active = menu_mode;
03632 
03633          currentmarked = conf->markedusers;
03634          if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03635              ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
03636              ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
03637              lastmarked == 0) {
03638             if (currentmarked == 1 && conf->users > 1) {
03639                ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03640                if (conf->users - 1 == 1) {
03641                   if (!ast_streamfile(chan, "conf-userwilljoin", ast_channel_language(chan))) {
03642                      ast_waitstream(chan, "");
03643                   }
03644                } else {
03645                   if (!ast_streamfile(chan, "conf-userswilljoin", ast_channel_language(chan))) {
03646                      ast_waitstream(chan, "");
03647                   }
03648                }
03649             }
03650             if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
03651                if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
03652                   ast_waitstream(chan, "");
03653                }
03654             }
03655          }
03656 
03657          /* Update the struct with the actual confflags */
03658          user->userflags = *confflags;
03659 
03660          if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
03661             if (currentmarked == 0) {
03662                if (lastmarked != 0) {
03663                   if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
03664                      if (!ast_streamfile(chan, "conf-leaderhasleft", ast_channel_language(chan))) {
03665                         ast_waitstream(chan, "");
03666                      }
03667                   }
03668                   if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
03669                      if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03670                         ret = 0;
03671                      }
03672                      break;
03673                   } else {
03674                      dahdic.confmode = DAHDI_CONF_CONF;
03675                      if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03676                         ast_log(LOG_WARNING, "Error setting conference\n");
03677                         close(fd);
03678                         goto outrun;
03679                      }
03680                   }
03681                }
03682                if (!musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
03683                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03684                   musiconhold = 1;
03685                }
03686             } else if (currentmarked >= 1 && lastmarked == 0) {
03687                /* Marked user entered, so cancel timeout */
03688                timeout = 0;
03689                if (ast_test_flag64(confflags, CONFFLAG_MONITOR)) {
03690                   dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
03691                } else if (ast_test_flag64(confflags, CONFFLAG_TALKER)) {
03692                   dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
03693                } else {
03694                   dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
03695                }
03696                if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03697                   ast_log(LOG_WARNING, "Error setting conference\n");
03698                   close(fd);
03699                   goto outrun;
03700                }
03701                if (musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
03702                   ast_moh_stop(chan);
03703                   musiconhold = 0;
03704                }
03705                if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && 
03706                   !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
03707                   if (!ast_streamfile(chan, "conf-placeintoconf", ast_channel_language(chan))) {
03708                      ast_waitstream(chan, "");
03709                   }
03710                   conf_play(chan, conf, ENTER);
03711                }
03712             }
03713          }
03714 
03715          /* trying to add moh for single person conf */
03716          if (ast_test_flag64(confflags, CONFFLAG_MOH) && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
03717             if (conf->users == 1) {
03718                if (!musiconhold) {
03719                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03720                   musiconhold = 1;
03721                } 
03722             } else {
03723                if (musiconhold) {
03724                   ast_moh_stop(chan);
03725                   musiconhold = 0;
03726                }
03727             }
03728          }
03729          
03730          /* Leave if the last marked user left */
03731          if (currentmarked == 0 && lastmarked != 0 && ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
03732             if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03733                ret = 0;
03734             } else {
03735                ret = -1;
03736             }
03737             break;
03738          }
03739 
03740          /* Throw a TestEvent if a user exit did not cause this user to leave the conference */
03741          if (conf->users != lastusers) {
03742             if (conf->users < lastusers) {
03743                ast_test_suite_event_notify("NOEXIT", "Message: CONFFLAG_MARKEDEXIT\r\nLastUsers: %d\r\nUsers: %d", lastusers, conf->users);
03744             }
03745             lastusers = conf->users;
03746          }
03747 
03748          /* Check if my modes have changed */
03749 
03750          /* If I should be muted but am still talker, mute me */
03751          if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
03752             dahdic.confmode ^= DAHDI_CONF_TALKER;
03753             if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03754                ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
03755                ret = -1;
03756                break;
03757             }
03758 
03759             /* Indicate user is not talking anymore - change him to unmonitored state */
03760             if (ast_test_flag64(confflags,  (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER))) {
03761                set_user_talking(chan, conf, user, -1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03762             }
03763             /*** DOCUMENTATION
03764             <managerEventInstance>
03765                <synopsis>Raised when a MeetMe user is muted.</synopsis>
03766                <syntax>
03767                   <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
03768                   <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Usernum'])" />
03769                   <parameter name="Status">
03770                      <enumlist>
03771                         <enum name="on"/>
03772                         <enum name="off"/>
03773                      </enumlist>
03774                   </parameter>
03775                </syntax>
03776             </managerEventInstance>
03777             ***/
03778             ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
03779                "Channel: %s\r\n"
03780                "Uniqueid: %s\r\n"
03781                "Meetme: %s\r\n"
03782                "Usernum: %d\r\n"
03783                "Status: on\r\n",
03784                ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
03785          }
03786 
03787          /* If I should be un-muted but am not talker, un-mute me */
03788          if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
03789             dahdic.confmode |= DAHDI_CONF_TALKER;
03790             if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03791                ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
03792                ret = -1;
03793                break;
03794             }
03795             /*** DOCUMENTATION
03796             <managerEventInstance>
03797                <synopsis>Raised when a MeetMe user is unmuted.</synopsis>
03798             </managerEventInstance>
03799             ***/
03800             ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
03801                "Channel: %s\r\n"
03802                "Uniqueid: %s\r\n"
03803                "Meetme: %s\r\n"
03804                "Usernum: %d\r\n"
03805                "Status: off\r\n",
03806                ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
03807          }
03808          
03809          if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
03810             (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
03811             talkreq_manager = 1;
03812 
03813             /*** DOCUMENTATION
03814             <managerEventInstance>
03815                <synopsis>Raised when a MeetMe user has started talking.</synopsis>
03816                <syntax>
03817                   <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
03818                   <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Usernum'])" />
03819                   <parameter name="Status">
03820                      <enumlist>
03821                         <enum name="on"/>
03822                         <enum name="off"/>
03823                      </enumlist>
03824                   </parameter>
03825                </syntax>
03826             </managerEventInstance>
03827             ***/
03828             ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
03829                "Channel: %s\r\n"
03830                "Uniqueid: %s\r\n"
03831                "Meetme: %s\r\n"
03832                "Usernum: %d\r\n"
03833                "Status: on\r\n",
03834                ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
03835          }
03836 
03837          if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
03838             !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
03839             talkreq_manager = 0;
03840             /*** DOCUMENTATION
03841             <managerEventInstance>
03842                <synopsis>Raised when a MeetMe user has finished talking.</synopsis>
03843             </managerEventInstance>
03844             ***/
03845             ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
03846                "Channel: %s\r\n"
03847                "Uniqueid: %s\r\n"
03848                "Meetme: %s\r\n"
03849                "Usernum: %d\r\n"
03850                "Status: off\r\n",
03851                ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
03852          }
03853 
03854          /* If user have been hung up, exit the conference */
03855          if (user->adminflags & ADMINFLAG_HANGUP) {
03856             ret = 0;
03857             break;
03858          }
03859 
03860          /* If I have been kicked, exit the conference */
03861          if (user->adminflags & ADMINFLAG_KICKME) {
03862             /* You have been kicked. */
03863             if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && 
03864                !ast_streamfile(chan, "conf-kicked", ast_channel_language(chan))) {
03865                ast_waitstream(chan, "");
03866             }
03867             ret = 0;
03868             break;
03869          }
03870 
03871          /* Perform a hangup check here since ast_waitfor_nandfds will not always be able to get a channel after a hangup has occurred */
03872          if (ast_check_hangup(chan)) {
03873             break;
03874          }
03875 
03876          c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
03877 
03878          if (c) {
03879             char dtmfstr[2] = "";
03880 
03881             if (ast_channel_fd(c, 0) != origfd || (user->dahdichannel && (ast_channel_audiohooks(c) || ast_channel_monitor(c)))) {
03882                if (using_pseudo) {
03883                   /* Kill old pseudo */
03884                   close(fd);
03885                   using_pseudo = 0;
03886                }
03887                ast_debug(1, "Ooh, something swapped out under us, starting over\n");
03888                retrydahdi = (strcasecmp(ast_channel_tech(c)->type, "DAHDI") || (ast_channel_audiohooks(c) || ast_channel_monitor(c)) ? 1 : 0);
03889                user->dahdichannel = !retrydahdi;
03890                goto dahdiretry;
03891             }
03892             if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
03893                f = ast_read_noaudio(c);
03894             } else {
03895                f = ast_read(c);
03896             }
03897             if (!f) {
03898                break;
03899             }
03900             if (f->frametype == AST_FRAME_DTMF) {
03901                dtmfstr[0] = f->subclass.integer;
03902                dtmfstr[1] = '\0';
03903             }
03904 
03905             if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.format.id == AST_FORMAT_SLINEAR)) {
03906                if (user->talk.actual) {
03907                   ast_frame_adjust_volume(f, user->talk.actual);
03908                }
03909 
03910                if (ast_test_flag64(confflags, (CONFFLAG_OPTIMIZETALKER | CONFFLAG_MONITORTALKER))) {
03911                   if (user->talking == -1) {
03912                      user->talking = 0;
03913                   }
03914 
03915                   res = ast_dsp_silence(dsp, f, &totalsilence);
03916                   if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
03917                      set_user_talking(chan, conf, user, 1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03918                   }
03919 
03920                   if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
03921                      set_user_talking(chan, conf, user, 0, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03922                   }
03923                }
03924                if (using_pseudo) {
03925                   /* Absolutely do _not_ use careful_write here...
03926                      it is important that we read data from the channel
03927                      as fast as it arrives, and feed it into the conference.
03928                      The buffering in the pseudo channel will take care of any
03929                      timing differences, unless they are so drastic as to lose
03930                      audio frames (in which case carefully writing would only
03931                      have delayed the audio even further).
03932                   */
03933                   /* As it turns out, we do want to use careful write.  We just
03934                      don't want to block, but we do want to at least *try*
03935                      to write out all the samples.
03936                    */
03937                   if (user->talking || !ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER)) {
03938                      careful_write(fd, f->data.ptr, f->datalen, 0);
03939                   }
03940                }
03941             } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '*') && ast_test_flag64(confflags, CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_mode)) {
03942                if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03943                   conf_queue_dtmf(conf, user, f);
03944                }
03945                /* Take out of conference */
03946                if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
03947                   ast_log(LOG_WARNING, "Error setting conference\n");
03948                   close(fd);
03949                   ast_frfree(f);
03950                   goto outrun;
03951                }
03952 
03953                /* if we are entering the menu, and the user has a channel-driver
03954                   volume adjustment, clear it
03955                */
03956                if (!menu_mode && user->talk.desired && !user->talk.actual) {
03957                   set_talk_volume(user, 0);
03958                }
03959 
03960                if (musiconhold) {
03961                   ast_moh_stop(chan);
03962                } else if (!menu_mode) {
03963                   char *menu_to_play;
03964                   if (ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03965                      menu_mode = MENU_ADMIN;
03966                      menu_to_play = "conf-adminmenu-18";
03967                   } else {
03968                      menu_mode = MENU_NORMAL;
03969                      menu_to_play = "conf-usermenu-162";
03970                   }
03971 
03972                   if (!ast_streamfile(chan, menu_to_play, ast_channel_language(chan))) {
03973                      dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
03974                      ast_stopstream(chan);
03975                   } else {
03976                      dtmf = 0;
03977                   }
03978                } else {
03979                   dtmf = f->subclass.integer;
03980                }
03981 
03982                if (dtmf > 0) {
03983                   meetme_menu(&menu_mode, &dtmf, conf, confflags,
03984                      chan, user, recordingtmp, sizeof(recordingtmp), cap_slin);
03985                }
03986 
03987                if (musiconhold && !menu_mode) {
03988                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03989                }
03990 
03991                /* Put back into conference */
03992                if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03993                   ast_log(LOG_WARNING, "Error setting conference\n");
03994                   close(fd);
03995                   ast_frfree(f);
03996                   goto outrun;
03997                }
03998 
03999                conf_flush(fd, chan);
04000             /*
04001              * Since options using DTMF could absorb DTMF meant for the
04002              * conference menu, we have to check them after the menu.
04003              */
04004             } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
04005                if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
04006                   conf_queue_dtmf(conf, user, f);
04007                }
04008 
04009                if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
04010                   ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
04011                   ret = 0;
04012                   ast_frfree(f);
04013                   break;
04014                } else {
04015                   ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
04016                }
04017             } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_KEYEXIT) &&
04018                (strchr(exitkeys, f->subclass.integer))) {
04019                pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
04020 
04021                if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
04022                   conf_queue_dtmf(conf, user, f);
04023                }
04024                ret = 0;
04025                ast_frfree(f);
04026                break;
04027             } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
04028                && ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
04029                conf_queue_dtmf(conf, user, f);
04030             } else if (ast_test_flag64(confflags, CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
04031                switch (f->subclass.integer) {
04032                case AST_CONTROL_HOLD:
04033                   sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
04034                   break;
04035                default:
04036                   break;
04037                }
04038             } else if (f->frametype == AST_FRAME_NULL) {
04039                /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
04040             } else if (f->frametype == AST_FRAME_CONTROL) {
04041                switch (f->subclass.integer) {
04042                case AST_CONTROL_BUSY:
04043                case AST_CONTROL_CONGESTION:
04044                   ast_frfree(f);
04045                   goto outrun;
04046                   break;
04047                default:
04048                   ast_debug(1,
04049                      "Got ignored control frame on channel %s, f->frametype=%d,f->subclass=%d\n",
04050                      ast_channel_name(chan), f->frametype, f->subclass.integer);
04051                }
04052             } else {
04053                ast_debug(1,
04054                   "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
04055                   ast_channel_name(chan), f->frametype, f->subclass.integer);
04056             }
04057             ast_frfree(f);
04058          } else if (outfd > -1) {
04059             res = read(outfd, buf, CONF_SIZE);
04060             if (res > 0) {
04061                memset(&fr, 0, sizeof(fr));
04062                fr.frametype = AST_FRAME_VOICE;
04063                ast_format_set(&fr.subclass.format, AST_FORMAT_SLINEAR, 0);
04064                fr.datalen = res;
04065                fr.samples = res / 2;
04066                fr.data.ptr = buf;
04067                fr.offset = AST_FRIENDLY_OFFSET;
04068                if (!user->listen.actual &&
04069                   (ast_test_flag64(confflags, CONFFLAG_MONITOR) ||
04070                    (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
04071                    (!user->talking && ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER))
04072                    )) {
04073                   int idx;
04074                   for (idx = 0; idx < AST_FRAME_BITS; idx++) {
04075                      if (ast_format_to_old_bitfield(ast_channel_rawwriteformat(chan)) & (1 << idx)) {
04076                         break;
04077                      }
04078                   }
04079                   if (idx >= AST_FRAME_BITS) {
04080                      goto bailoutandtrynormal;
04081                   }
04082                   ast_mutex_lock(&conf->listenlock);
04083                   if (!conf->transframe[idx]) {
04084                      if (conf->origframe) {
04085                         if (musiconhold
04086                            && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)
04087                            && !ast_dsp_silence(dsp, conf->origframe, &confsilence)
04088                            && confsilence < MEETME_DELAYDETECTTALK) {
04089                            ast_moh_stop(chan);
04090                            mohtempstopped = 1;
04091                         }
04092                         if (!conf->transpath[idx]) {
04093                            struct ast_format src;
04094                            struct ast_format dst;
04095                            ast_format_set(&src, AST_FORMAT_SLINEAR, 0);
04096                            ast_format_from_old_bitfield(&dst, (1 << idx));
04097                            conf->transpath[idx] = ast_translator_build_path(&dst, &src);
04098                         }
04099                         if (conf->transpath[idx]) {
04100                            conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
04101                            if (!conf->transframe[idx]) {
04102                               conf->transframe[idx] = &ast_null_frame;
04103                            }
04104                         }
04105                      }
04106                   }
04107                   if (conf->transframe[idx]) {
04108                      if ((conf->transframe[idx]->frametype != AST_FRAME_NULL) &&
04109                          can_write(chan, confflags)) {
04110                         struct ast_frame *cur;
04111                         /* the translator may have returned a list of frames, so
04112                            write each one onto the channel
04113                         */
04114                         for (cur = conf->transframe[idx]; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
04115                            if (ast_write(chan, cur)) {
04116                               ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", ast_channel_name(chan));
04117                               break;
04118                            }
04119                         }
04120                         if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
04121                            mohtempstopped = 0;
04122                            conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
04123                         }
04124                      }
04125                   } else {
04126                      ast_mutex_unlock(&conf->listenlock);
04127                      goto bailoutandtrynormal;
04128                   }
04129                   ast_mutex_unlock(&conf->listenlock);
04130                } else {
04131 bailoutandtrynormal:
04132                   if (musiconhold
04133                      && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)
04134                      && !ast_dsp_silence(dsp, &fr, &confsilence)
04135                      && confsilence < MEETME_DELAYDETECTTALK) {
04136                      ast_moh_stop(chan);
04137                      mohtempstopped = 1;
04138                   }
04139                   if (user->listen.actual) {
04140                      ast_frame_adjust_volume(&fr, user->listen.actual);
04141                   }
04142                   if (can_write(chan, confflags) && ast_write(chan, &fr) < 0) {
04143                      ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", ast_channel_name(chan));
04144                   }
04145                   if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
04146                      mohtempstopped = 0;
04147                      conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
04148                   }
04149                }
04150             } else {
04151                ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
04152             }
04153          }
04154          lastmarked = currentmarked;
04155       }
04156    }
04157 
04158    if (musiconhold) {
04159       ast_moh_stop(chan);
04160    }
04161    
04162    if (using_pseudo) {
04163       close(fd);
04164    } else {
04165       /* Take out of conference */
04166       dahdic.chan = 0;  
04167       dahdic.confno = 0;
04168       dahdic.confmode = 0;
04169       if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
04170          ast_log(LOG_WARNING, "Error setting conference\n");
04171       }
04172    }
04173 
04174    reset_volumes(user);
04175 
04176    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
04177       !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
04178       conf_play(chan, conf, LEAVE);
04179    }
04180 
04181    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_INTROUSER |CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC) && conf->users > 1) {
04182       struct announce_listitem *item;
04183       if (!(item = ao2_alloc(sizeof(*item), NULL)))
04184          goto outrun;
04185       ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
04186       ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
04187       item->confchan = conf->chan;
04188       item->confusers = conf->users;
04189       item->announcetype = CONF_HASLEFT;
04190       if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
04191          item->vmrec = 1;
04192       }
04193       ast_mutex_lock(&conf->announcelistlock);
04194       AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
04195       ast_cond_signal(&conf->announcelist_addition);
04196       ast_mutex_unlock(&conf->announcelistlock);
04197    } 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) {
04198       /* Last person is leaving, so no reason to try and announce, but should delete the name recording */
04199       ast_filedelete(user->namerecloc, NULL);
04200    }
04201 
04202  outrun:
04203    AST_LIST_LOCK(&confs);
04204 
04205    if (dsp) {
04206       ast_dsp_free(dsp);
04207    }
04208    
04209    if (user->user_no) {
04210       /* Only cleanup users who really joined! */
04211       now = ast_tvnow();
04212 
04213       if (sent_event) {
04214          /*** DOCUMENTATION
04215          <managerEventInstance>
04216             <synopsis>Raised when a user leaves a MeetMe conference.</synopsis>
04217             <syntax>
04218                <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
04219                <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Usernum'])" />
04220                <parameter name="Duration">
04221                   <para>The length of time in seconds that the Meetme user was in the conference.</para>
04222                </parameter>
04223             </syntax>
04224             <see-also>
04225                <ref type="managerEvent">MeetmeJoin</ref>
04226             </see-also>
04227          </managerEventInstance>
04228          ***/
04229          ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeLeave",
04230             "Channel: %s\r\n"
04231             "Uniqueid: %s\r\n"
04232             "Meetme: %s\r\n"
04233             "Usernum: %d\r\n"
04234             "CallerIDNum: %s\r\n"
04235             "CallerIDName: %s\r\n"
04236             "ConnectedLineNum: %s\r\n"
04237             "ConnectedLineName: %s\r\n"
04238             "Duration: %ld\r\n",
04239             ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno,
04240             user->user_no,
04241             S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
04242             S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<unknown>"),
04243             S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, "<unknown>"),
04244             S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "<unknown>"),
04245             (long)(now.tv_sec - user->jointime));
04246       }
04247 
04248       if (setusercount) {
04249          conf->users--;
04250          if (rt_log_members) {
04251             /* Update table */
04252             snprintf(members, sizeof(members), "%d", conf->users);
04253             ast_realtime_require_field("meetme",
04254                "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
04255                "members", RQ_UINTEGER1, strlen(members),
04256                NULL);
04257             ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
04258          }
04259          if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
04260             conf->markedusers--;
04261          }
04262       }
04263       /* Remove ourselves from the container */
04264       ao2_unlink(conf->usercontainer, user); 
04265 
04266       /* Change any states */
04267       if (!conf->users) {
04268          ast_devstate_changed(AST_DEVICE_NOT_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
04269       }
04270 
04271       /* This flag is meant to kill a conference with only one participant remaining.  */
04272       if (conf->users == 1 && ast_test_flag64(confflags, CONFFLAG_KILL_LAST_MAN_STANDING)) {
04273          ao2_callback(conf->usercontainer, 0, user_set_hangup_cb, NULL);
04274       }
04275 
04276       /* Return the number of seconds the user was in the conf */
04277       snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
04278       pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
04279 
04280       /* Return the RealTime bookid for CDR linking */
04281       if (rt_schedule) {
04282          pbx_builtin_setvar_helper(chan, "MEETMEBOOKID", conf->bookid);
04283       }
04284    }
04285    ao2_ref(user, -1);
04286    AST_LIST_UNLOCK(&confs);
04287 
04288 
04289 conf_run_cleanup:
04290    cap_slin = ast_format_cap_destroy(cap_slin);
04291 
04292    return ret;
04293 }
04294 
04295 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
04296             char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags, int *too_early, char **optargs)
04297 {
04298    struct ast_variable *var, *origvar;
04299    struct ast_conference *cnf;
04300 
04301    *too_early = 0;
04302 
04303    /* Check first in the conference list */
04304    AST_LIST_LOCK(&confs);
04305    AST_LIST_TRAVERSE(&confs, cnf, list) {
04306       if (!strcmp(confno, cnf->confno)) {
04307          break;
04308       }
04309    }
04310    if (cnf) {
04311       cnf->refcount += refcount;
04312    }
04313    AST_LIST_UNLOCK(&confs);
04314 
04315    if (!cnf) {
04316       char *pin = NULL, *pinadmin = NULL; /* For temp use */
04317       int maxusers = 0;
04318       struct timeval now;
04319       char recordingfilename[256] = "";
04320       char recordingformat[11] = "";
04321       char currenttime[32] = "";
04322       char eatime[32] = "";
04323       char bookid[51] = "";
04324       char recordingtmp[AST_MAX_EXTENSION] = "";
04325       char useropts[OPTIONS_LEN + 1] = ""; /* Used for RealTime conferences */
04326       char adminopts[OPTIONS_LEN + 1] = "";
04327       struct ast_tm tm, etm;
04328       struct timeval endtime = { .tv_sec = 0 };
04329       const char *var2;
04330 
04331       if (rt_schedule) {
04332          now = ast_tvnow();
04333 
04334          ast_localtime(&now, &tm, NULL);
04335          ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
04336 
04337          ast_debug(1, "Looking for conference %s that starts after %s\n", confno, currenttime);
04338 
04339          var = ast_load_realtime("meetme", "confno",
04340             confno, "starttime <= ", currenttime, "endtime >= ",
04341             currenttime, NULL);
04342 
04343          if (!var && fuzzystart) {
04344             now = ast_tvnow();
04345             now.tv_sec += fuzzystart;
04346 
04347             ast_localtime(&now, &tm, NULL);
04348             ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
04349             var = ast_load_realtime("meetme", "confno",
04350                confno, "starttime <= ", currenttime, "endtime >= ",
04351                currenttime, NULL);
04352          }
04353 
04354          if (!var && earlyalert) {
04355             now = ast_tvnow();
04356             now.tv_sec += earlyalert;
04357             ast_localtime(&now, &etm, NULL);
04358             ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
04359             var = ast_load_realtime("meetme", "confno",
04360                confno, "starttime <= ", eatime, "endtime >= ",
04361                currenttime, NULL);
04362             if (var) {
04363                *too_early = 1;
04364             }
04365          }
04366 
04367       } else {
04368           var = ast_load_realtime("meetme", "confno", confno, NULL);
04369       }
04370 
04371       if (!var) {
04372          return NULL;
04373       }
04374 
04375       if (rt_schedule && *too_early) {
04376          /* Announce that the caller is early and exit */
04377          if (!ast_streamfile(chan, "conf-has-not-started", ast_channel_language(chan))) {
04378             ast_waitstream(chan, "");
04379          }
04380          ast_variables_destroy(var);
04381          return NULL;
04382       }
04383 
04384       for (origvar = var; var; var = var->next) {
04385          if (!strcasecmp(var->name, "pin")) {
04386             pin = ast_strdupa(var->value);
04387          } else if (!strcasecmp(var->name, "adminpin")) {
04388             pinadmin = ast_strdupa(var->value);
04389          } else if (!strcasecmp(var->name, "bookId")) {
04390             ast_copy_string(bookid, var->value, sizeof(bookid));
04391          } else if (!strcasecmp(var->name, "opts")) {
04392             ast_copy_string(useropts, var->value, sizeof(char[OPTIONS_LEN + 1]));
04393          } else if (!strcasecmp(var->name, "maxusers")) {
04394             maxusers = atoi(var->value);
04395          } else if (!strcasecmp(var->name, "adminopts")) {
04396             ast_copy_string(adminopts, var->value, sizeof(char[OPTIONS_LEN + 1]));
04397          } else if (!strcasecmp(var->name, "recordingfilename")) {
04398             ast_copy_string(recordingfilename, var->value, sizeof(recordingfilename));
04399          } else if (!strcasecmp(var->name, "recordingformat")) {
04400             ast_copy_string(recordingformat, var->value, sizeof(recordingformat));
04401          } else if (!strcasecmp(var->name, "endtime")) {
04402             struct ast_tm endtime_tm;
04403             ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
04404             endtime = ast_mktime(&endtime_tm, NULL);
04405          }
04406       }
04407 
04408       ast_variables_destroy(origvar);
04409 
04410       cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan, NULL);
04411 
04412       if (cnf) {
04413          struct ast_flags64 tmp_flags;
04414 
04415          cnf->maxusers = maxusers;
04416          cnf->endalert = endalert;
04417          cnf->endtime = endtime.tv_sec;
04418          cnf->useropts = ast_strdup(useropts);
04419          cnf->adminopts = ast_strdup(adminopts);
04420          cnf->bookid = ast_strdup(bookid);
04421          if (!ast_strlen_zero(recordingfilename)) {
04422             cnf->recordingfilename = ast_strdup(recordingfilename);
04423          }
04424          if (!ast_strlen_zero(recordingformat)) {
04425             cnf->recordingformat = ast_strdup(recordingformat);
04426          }
04427 
04428          /* Parse the other options into confflags -- need to do this in two
04429           * steps, because the parse_options routine zeroes the buffer. */
04430          ast_app_parse_options64(meetme_opts, &tmp_flags, optargs, useropts);
04431          ast_copy_flags64(confflags, &tmp_flags, tmp_flags.flags);
04432 
04433          if (strchr(cnf->useropts, 'r')) {
04434             if (ast_strlen_zero(recordingfilename)) { /* If the recordingfilename in the database is empty, use the channel definition or use the default. */
04435                ast_channel_lock(chan);
04436                if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
04437                   ast_free(cnf->recordingfilename);
04438                   cnf->recordingfilename = ast_strdup(var2);
04439                }
04440                ast_channel_unlock(chan);
04441                if (ast_strlen_zero(cnf->recordingfilename)) {
04442                   snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", cnf->confno, ast_channel_uniqueid(chan));
04443                   ast_free(cnf->recordingfilename);
04444                   cnf->recordingfilename = ast_strdup(recordingtmp);
04445                }
04446             }
04447             if (ast_strlen_zero(cnf->recordingformat)) {/* If the recording format is empty, use the wav as default */
04448                ast_channel_lock(chan);
04449                if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
04450                   ast_free(cnf->recordingformat);
04451                   cnf->recordingformat = ast_strdup(var2);
04452                }
04453                ast_channel_unlock(chan);
04454                if (ast_strlen_zero(cnf->recordingformat)) {
04455                   ast_free(cnf->recordingformat);
04456                   cnf->recordingformat = ast_strdup("wav");
04457                }
04458             }
04459             ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
04460          }
04461       }
04462    }
04463 
04464    if (cnf) {
04465       if (confflags->flags && !cnf->chan &&
04466           !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
04467           ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW) | CONFFLAG_INTROUSER_VMREC) {
04468          ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
04469          ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC);
04470       }
04471 
04472       if (confflags && !cnf->chan &&
04473           ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
04474          ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
04475          ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
04476       }
04477    }
04478 
04479    return cnf;
04480 }
04481 
04482 
04483 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
04484                char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags)
04485 {
04486    struct ast_config *cfg;
04487    struct ast_variable *var;
04488    struct ast_flags config_flags = { 0 };
04489    struct ast_conference *cnf;
04490 
04491    AST_DECLARE_APP_ARGS(args,
04492       AST_APP_ARG(confno);
04493       AST_APP_ARG(pin);
04494       AST_APP_ARG(pinadmin);
04495    );
04496 
04497    /* Check first in the conference list */
04498    ast_debug(1, "The requested confno is '%s'?\n", confno);
04499    AST_LIST_LOCK(&confs);
04500    AST_LIST_TRAVERSE(&confs, cnf, list) {
04501       ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
04502       if (!strcmp(confno, cnf->confno))
04503          break;
04504    }
04505    if (cnf) {
04506       cnf->refcount += refcount;
04507    }
04508    AST_LIST_UNLOCK(&confs);
04509 
04510    if (!cnf) {
04511       if (dynamic) {
04512          /* No need to parse meetme.conf */
04513          ast_debug(1, "Building dynamic conference '%s'\n", confno);
04514          if (dynamic_pin) {
04515             if (dynamic_pin[0] == 'q') {
04516                /* Query the user to enter a PIN */
04517                if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
04518                   return NULL;
04519             }
04520             cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan, NULL);
04521          } else {
04522             cnf = build_conf(confno, "", "", make, dynamic, refcount, chan, NULL);
04523          }
04524       } else {
04525          /* Check the config */
04526          cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04527          if (!cfg) {
04528             ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
04529             return NULL;
04530          } else if (cfg == CONFIG_STATUS_FILEINVALID) {
04531             ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format.  Aborting.\n");
04532             return NULL;
04533          }
04534 
04535          for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
04536             char parse[MAX_SETTINGS];
04537 
04538             if (strcasecmp(var->name, "conf"))
04539                continue;
04540 
04541             ast_copy_string(parse, var->value, sizeof(parse));
04542 
04543             AST_STANDARD_APP_ARGS(args, parse);
04544             ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
04545             if (!strcasecmp(args.confno, confno)) {
04546                /* Bingo it's a valid conference */
04547                cnf = build_conf(args.confno,
04548                      S_OR(args.pin, ""),
04549                      S_OR(args.pinadmin, ""),
04550                      make, dynamic, refcount, chan, NULL);
04551                break;
04552             }
04553          }
04554          if (!var) {
04555             ast_debug(1, "%s isn't a valid conference\n", confno);
04556          }
04557          ast_config_destroy(cfg);
04558       }
04559    } else if (dynamic_pin) {
04560       /* Correct for the user selecting 'D' instead of 'd' to have
04561          someone join into a conference that has already been created
04562          with a pin. */
04563       if (dynamic_pin[0] == 'q') {
04564          dynamic_pin[0] = '\0';
04565       }
04566    }
04567 
04568    if (cnf) {
04569       if (confflags && !cnf->chan &&
04570           !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
04571           ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW  | CONFFLAG_INTROUSER_VMREC)) {
04572          ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
04573          ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC);
04574       }
04575       
04576       if (confflags && !cnf->chan &&
04577           ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
04578          ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
04579          ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
04580       }
04581    }
04582 
04583    return cnf;
04584 }
04585 
04586 /*! \brief The MeetmeCount application */
04587 static int count_exec(struct ast_channel *chan, const char *data)
04588 {
04589    int res = 0;
04590    struct ast_conference *conf;
04591    int count;
04592    char *localdata;
04593    char val[80] = "0"; 
04594    AST_DECLARE_APP_ARGS(args,
04595       AST_APP_ARG(confno);
04596       AST_APP_ARG(varname);
04597    );
04598 
04599    if (ast_strlen_zero(data)) {
04600       ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
04601       return -1;
04602    }
04603    
04604    localdata = ast_strdupa(data);
04605 
04606    AST_STANDARD_APP_ARGS(args, localdata);
04607    
04608    conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
04609 
04610    if (conf) {
04611       count = conf->users;
04612       dispose_conf(conf);
04613       conf = NULL;
04614    } else
04615       count = 0;
04616 
04617    if (!ast_strlen_zero(args.varname)) {
04618       /* have var so load it and exit */
04619       snprintf(val, sizeof(val), "%d", count);
04620       pbx_builtin_setvar_helper(chan, args.varname, val);
04621    } else {
04622       if (ast_channel_state(chan) != AST_STATE_UP) {
04623          ast_answer(chan);
04624       }
04625       res = ast_say_number(chan, count, "", ast_channel_language(chan), (char *) NULL); /* Needs gender */
04626    }
04627 
04628    return res;
04629 }
04630 
04631 /*! \brief The meetme() application */
04632 static int conf_exec(struct ast_channel *chan, const char *data)
04633 {
04634    int res = -1;
04635    char confno[MAX_CONFNUM] = "";
04636    int allowretry = 0;
04637    int retrycnt = 0;
04638    struct ast_conference *cnf = NULL;
04639    struct ast_flags64 confflags = {0};
04640    struct ast_flags config_flags = { 0 };
04641    int dynamic = 0;
04642    int empty = 0, empty_no_pin = 0;
04643    int always_prompt = 0;
04644    const char *notdata;
04645    char *info, the_pin[MAX_PIN] = "";
04646    AST_DECLARE_APP_ARGS(args,
04647       AST_APP_ARG(confno);
04648       AST_APP_ARG(options);
04649       AST_APP_ARG(pin);
04650    );
04651    char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
04652 
04653    if (ast_strlen_zero(data)) {
04654       allowretry = 1;
04655       notdata = "";
04656    } else {
04657       notdata = data;
04658    }
04659    
04660    if (ast_channel_state(chan) != AST_STATE_UP)
04661       ast_answer(chan);
04662 
04663    info = ast_strdupa(notdata);
04664 
04665    AST_STANDARD_APP_ARGS(args, info);  
04666 
04667    if (args.confno) {
04668       ast_copy_string(confno, args.confno, sizeof(confno));
04669       if (ast_strlen_zero(confno)) {
04670          allowretry = 1;
04671       }
04672    }
04673    
04674    if (args.pin)
04675       ast_copy_string(the_pin, args.pin, sizeof(the_pin));
04676 
04677    if (args.options) {
04678       ast_app_parse_options64(meetme_opts, &confflags, optargs, args.options);
04679       dynamic = ast_test_flag64(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
04680       if (ast_test_flag64(&confflags, CONFFLAG_DYNAMICPIN) && ast_strlen_zero(args.pin))
04681          strcpy(the_pin, "q");
04682 
04683       empty = ast_test_flag64(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
04684       empty_no_pin = ast_test_flag64(&confflags, CONFFLAG_EMPTYNOPIN);
04685       always_prompt = ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT | CONFFLAG_DYNAMICPIN);
04686    }
04687 
04688    do {
04689       if (retrycnt > 3)
04690          allowretry = 0;
04691       if (empty) {
04692          int i;
04693          struct ast_config *cfg;
04694          struct ast_variable *var;
04695          int confno_int;
04696 
04697          /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
04698          if ((empty_no_pin) || (!dynamic)) {
04699             cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04700             if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
04701                var = ast_variable_browse(cfg, "rooms");
04702                while (var) {
04703                   char parse[MAX_SETTINGS], *stringp = parse, *confno_tmp;
04704                   if (!strcasecmp(var->name, "conf")) {
04705                      int found = 0;
04706                      ast_copy_string(parse, var->value, sizeof(parse));
04707                      confno_tmp = strsep(&stringp, "|,");
04708                      if (!dynamic) {
04709                         /* For static:  run through the list and see if this conference is empty */
04710                         AST_LIST_LOCK(&confs);
04711                         AST_LIST_TRAVERSE(&confs, cnf, list) {
04712                            if (!strcmp(confno_tmp, cnf->confno)) {
04713                               /* The conference exists, therefore it's not empty */
04714                               found = 1;
04715                               break;
04716                            }
04717                         }
04718                         AST_LIST_UNLOCK(&confs);
04719                         cnf = NULL;
04720                         if (!found) {
04721                            /* At this point, we have a confno_tmp (static conference) that is empty */
04722                            if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
04723                               /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
04724                                * Case 2:  empty_no_pin and pin is blank (but not NULL)
04725                                * Case 3:  not empty_no_pin
04726                                */
04727                               ast_copy_string(confno, confno_tmp, sizeof(confno));
04728                               break;
04729                            }
04730                         }
04731                      }
04732                   }
04733                   var = var->next;
04734                }
04735                ast_config_destroy(cfg);
04736             }
04737 
04738             if (ast_strlen_zero(confno) && (cfg = ast_load_realtime_multientry("meetme", "confno LIKE", "%", SENTINEL))) {
04739                const char *catg;
04740                for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
04741                   const char *confno_tmp = ast_variable_retrieve(cfg, catg, "confno");
04742                   const char *pin_tmp = ast_variable_retrieve(cfg, catg, "pin");
04743                   if (ast_strlen_zero(confno_tmp)) {
04744                      continue;
04745                   }
04746                   if (!dynamic) {
04747                      int found = 0;
04748                      /* For static:  run through the list and see if this conference is empty */
04749                      AST_LIST_LOCK(&confs);
04750                      AST_LIST_TRAVERSE(&confs, cnf, list) {
04751                         if (!strcmp(confno_tmp, cnf->confno)) {
04752                            /* The conference exists, therefore it's not empty */
04753                            found = 1;
04754                            break;
04755                         }
04756                      }
04757                      AST_LIST_UNLOCK(&confs);
04758                      if (!found) {
04759                         /* At this point, we have a confno_tmp (realtime conference) that is empty */
04760                         if ((empty_no_pin && ast_strlen_zero(pin_tmp)) || (!empty_no_pin)) {
04761                            /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
04762                             * Case 2:  empty_no_pin and pin is blank (but not NULL)
04763                             * Case 3:  not empty_no_pin
04764                             */
04765                            ast_copy_string(confno, confno_tmp, sizeof(confno));
04766                            break;
04767                         }
04768                      }
04769                   }
04770                }
04771                ast_config_destroy(cfg);
04772             }
04773          }
04774 
04775          /* Select first conference number not in use */
04776          if (ast_strlen_zero(confno) && dynamic) {
04777             AST_LIST_LOCK(&confs);
04778             for (i = 0; i < ARRAY_LEN(conf_map); i++) {
04779                if (!conf_map[i]) {
04780                   snprintf(confno, sizeof(confno), "%d", i);
04781                   conf_map[i] = 1;
04782                   break;
04783                }
04784             }
04785             AST_LIST_UNLOCK(&confs);
04786          }
04787 
04788          /* Not found? */
04789          if (ast_strlen_zero(confno)) {
04790             res = ast_streamfile(chan, "conf-noempty", ast_channel_language(chan));
04791             ast_test_suite_event_notify("PLAYBACK", "Message: conf-noempty");
04792             if (!res)
04793                ast_waitstream(chan, "");
04794          } else {
04795             if (sscanf(confno, "%30d", &confno_int) == 1) {
04796                if (!ast_test_flag64(&confflags, CONFFLAG_QUIET)) {
04797                   res = ast_streamfile(chan, "conf-enteringno", ast_channel_language(chan));
04798                   if (!res) {
04799                      ast_waitstream(chan, "");
04800                      res = ast_say_digits(chan, confno_int, "", ast_channel_language(chan));
04801                   }
04802                }
04803             } else {
04804                ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
04805             }
04806          }
04807       }
04808 
04809       while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
04810          /* Prompt user for conference number */
04811          res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
04812          if (res < 0) {
04813             /* Don't try to validate when we catch an error */
04814             confno[0] = '\0';
04815             allowretry = 0;
04816             break;
04817          }
04818       }
04819       if (!ast_strlen_zero(confno)) {
04820          /* Check the validity of the conference */
04821          cnf = find_conf(chan, confno, 1, dynamic, the_pin, 
04822             sizeof(the_pin), 1, &confflags);
04823          if (!cnf) {
04824             int too_early = 0;
04825 
04826             cnf = find_conf_realtime(chan, confno, 1, dynamic, 
04827                the_pin, sizeof(the_pin), 1, &confflags, &too_early, optargs);
04828             if (rt_schedule && too_early)
04829                allowretry = 0;
04830          }
04831 
04832          if (!cnf) {
04833             if (allowretry) {
04834                confno[0] = '\0';
04835                res = ast_streamfile(chan, "conf-invalid", ast_channel_language(chan));
04836                if (!res)
04837                   ast_waitstream(chan, "");
04838                res = -1;
04839             }
04840          } else {
04841             /* Conference requires a pin for specified access level */
04842             int req_pin = !ast_strlen_zero(cnf->pin) ||
04843                (!ast_strlen_zero(cnf->pinadmin) &&
04844                   ast_test_flag64(&confflags, CONFFLAG_ADMIN));
04845             /* The following logic was derived from a
04846              * 4 variable truth table and defines which
04847              * circumstances are not exempt from pin
04848              * checking.
04849              * If this needs to be modified, write the
04850              * truth table back out from the boolean
04851              * expression AB+A'D+C', change the erroneous
04852              * result, and rederive the expression.
04853              * Variables:
04854              *  A: pin provided?
04855              *  B: always prompt?
04856              *  C: dynamic?
04857              *  D: has users? */
04858             int not_exempt = !cnf->isdynamic;
04859             not_exempt = not_exempt || (!ast_strlen_zero(args.pin) && ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT));
04860             not_exempt = not_exempt || (ast_strlen_zero(args.pin) && cnf->users);
04861             if (req_pin && not_exempt) {
04862                char pin[MAX_PIN] = "";
04863                int j;
04864 
04865                /* Allow the pin to be retried up to 3 times */
04866                for (j = 0; j < 3; j++) {
04867                   if (*the_pin && (always_prompt == 0)) {
04868                      ast_copy_string(pin, the_pin, sizeof(pin));
04869                      res = 0;
04870                   } else {
04871                      /* Prompt user for pin if pin is required */
04872                      ast_test_suite_event_notify("PLAYBACK", "Message: conf-getpin\r\n"
04873                         "Channel: %s",
04874                         ast_channel_name(chan));
04875                      res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
04876                   }
04877                   if (res >= 0) {
04878                      if ((!strcasecmp(pin, cnf->pin) &&
04879                           (ast_strlen_zero(cnf->pinadmin) ||
04880                            !ast_test_flag64(&confflags, CONFFLAG_ADMIN))) ||
04881                           (!ast_strlen_zero(cnf->pinadmin) &&
04882                            !strcasecmp(pin, cnf->pinadmin))) {
04883                         /* Pin correct */
04884                         allowretry = 0;
04885                         if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) {
04886                            if (!ast_strlen_zero(cnf->adminopts)) {
04887                               char *opts = ast_strdupa(cnf->adminopts);
04888                               ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04889                            }
04890                         } else {
04891                            if (!ast_strlen_zero(cnf->useropts)) {
04892                               char *opts = ast_strdupa(cnf->useropts);
04893                               ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04894                            }
04895                         }
04896                         /* Run the conference */
04897                         ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
04898                         res = conf_run(chan, cnf, &confflags, optargs);
04899                         break;
04900                      } else {
04901                         /* Pin invalid */
04902                         if (!ast_streamfile(chan, "conf-invalidpin", ast_channel_language(chan))) {
04903                            res = ast_waitstream(chan, AST_DIGIT_ANY);
04904                            ast_stopstream(chan);
04905                         } else {
04906                            ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
04907                            break;
04908                         }
04909                         if (res < 0)
04910                            break;
04911                         pin[0] = res;
04912                         pin[1] = '\0';
04913                         res = -1;
04914                         if (allowretry)
04915                            confno[0] = '\0';
04916                      }
04917                   } else {
04918                      /* failed when getting the pin */
04919                      res = -1;
04920                      allowretry = 0;
04921                      /* see if we need to get rid of the conference */
04922                      break;
04923                   }
04924 
04925                   /* Don't retry pin with a static pin */
04926                   if (*the_pin && (always_prompt == 0)) {
04927                      break;
04928                   }
04929                }
04930             } else {
04931                /* No pin required */
04932                allowretry = 0;
04933 
04934                /* For RealTime conferences without a pin 
04935                 * should still support loading options
04936                 */
04937                if (!ast_strlen_zero(cnf->useropts)) {
04938                   char *opts = ast_strdupa(cnf->useropts);
04939                   ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04940                }
04941 
04942                /* Run the conference */
04943                res = conf_run(chan, cnf, &confflags, optargs);
04944             }
04945             dispose_conf(cnf);
04946             cnf = NULL;
04947          }
04948       }
04949    } while (allowretry);
04950 
04951    if (cnf)
04952       dispose_conf(cnf);
04953    
04954    return res;
04955 }
04956 
04957 static struct ast_conf_user *find_user(struct ast_conference *conf, const char *callerident)
04958 {
04959    struct ast_conf_user *user = NULL;
04960    int cid;
04961 
04962    if (conf && callerident && sscanf(callerident, "%30d", &cid) == 1) {
04963       user = ao2_find(conf->usercontainer, &cid, 0);
04964       /* reference decremented later in admin_exec */
04965       return user;
04966    }
04967    return NULL;
04968 }
04969 
04970 static int user_listen_volup_cb(void *obj, void *unused, int flags)
04971 {
04972    struct ast_conf_user *user = obj;
04973    tweak_listen_volume(user, VOL_UP);
04974    return 0;
04975 }
04976 
04977 static int user_listen_voldown_cb(void *obj, void *unused, int flags)
04978 {
04979    struct ast_conf_user *user = obj;
04980    tweak_listen_volume(user, VOL_DOWN);
04981    return 0;
04982 }
04983 
04984 static int user_talk_volup_cb(void *obj, void *unused, int flags)
04985 {
04986    struct ast_conf_user *user = obj;
04987    tweak_talk_volume(user, VOL_UP);
04988    return 0;
04989 }
04990 
04991 static int user_talk_voldown_cb(void *obj, void *unused, int flags)
04992 {
04993    struct ast_conf_user *user = obj;
04994    tweak_talk_volume(user, VOL_DOWN);
04995    return 0;
04996 }
04997 
04998 static int user_reset_vol_cb(void *obj, void *unused, int flags)
04999 {
05000    struct ast_conf_user *user = obj;
05001    reset_volumes(user);
05002    return 0;
05003 }
05004 
05005 static int user_chan_cb(void *obj, void *args, int flags)
05006 {
05007    struct ast_conf_user *user = obj;
05008    const char *channel = args;
05009 
05010    if (!strcmp(ast_channel_name(user->chan), channel)) {
05011       return (CMP_MATCH | CMP_STOP);
05012    }
05013 
05014    return 0;
05015 }
05016 
05017 /*! \brief The MeetMeadmin application 
05018 
05019   MeetMeAdmin(confno, command, caller) */
05020 static int admin_exec(struct ast_channel *chan, const char *data) {
05021    char *params;
05022    struct ast_conference *cnf;
05023    struct ast_conf_user *user = NULL;
05024    AST_DECLARE_APP_ARGS(args,
05025       AST_APP_ARG(confno);
05026       AST_APP_ARG(command);
05027       AST_APP_ARG(user);
05028    );
05029    int res = 0;
05030 
05031    if (ast_strlen_zero(data)) {
05032       ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
05033       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
05034       return -1;
05035    }
05036 
05037    params = ast_strdupa(data);
05038    AST_STANDARD_APP_ARGS(args, params);
05039 
05040    if (!args.command) {
05041       ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
05042       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
05043       return -1;
05044    }
05045 
05046    AST_LIST_LOCK(&confs);
05047    AST_LIST_TRAVERSE(&confs, cnf, list) {
05048       if (!strcmp(cnf->confno, args.confno))
05049          break;
05050    }
05051 
05052    if (!cnf) {
05053       ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
05054       AST_LIST_UNLOCK(&confs);
05055       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOTFOUND");
05056       return 0;
05057    }
05058 
05059    ast_atomic_fetchadd_int(&cnf->refcount, 1);
05060 
05061    if (args.user) {
05062       user = find_user(cnf, args.user);
05063       if (!user) {
05064          ast_log(LOG_NOTICE, "Specified User not found!\n");
05065          res = -2;
05066          goto usernotfound;
05067       }
05068    } else {
05069       /* fail for commands that require a user */
05070       switch (*args.command) {
05071       case 'm': /* Unmute */
05072       case 'M': /* Mute */
05073       case 't': /* Lower user's talk volume */
05074       case 'T': /* Raise user's talk volume */
05075       case 'u': /* Lower user's listen volume */
05076       case 'U': /* Raise user's listen volume */
05077       case 'r': /* Reset user's volume level */
05078       case 'k': /* Kick user */
05079          res = -2;
05080          ast_log(LOG_NOTICE, "No user specified!\n");
05081          goto usernotfound;
05082       default:
05083          break;
05084       }
05085    }
05086 
05087    switch (*args.command) {
05088    case 76: /* L: Lock */ 
05089       cnf->locked = 1;
05090       break;
05091    case 108: /* l: Unlock */ 
05092       cnf->locked = 0;
05093       break;
05094    case 75: /* K: kick all users */
05095       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_kickme_cb, NULL);
05096       break;
05097    case 101: /* e: Eject last user*/
05098    {
05099       int max_no = 0;
05100       RAII_VAR(struct ast_conf_user *, eject_user, NULL, ao2_cleanup);
05101 
05102       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
05103       eject_user = ao2_find(cnf->usercontainer, &max_no, 0);
05104       if (!eject_user) {
05105          res = -1;
05106          ast_log(LOG_NOTICE, "No last user to kick!\n");
05107          break;
05108       }
05109 
05110       if (!ast_test_flag64(&eject_user->userflags, CONFFLAG_ADMIN)) {
05111          eject_user->adminflags |= ADMINFLAG_KICKME;
05112       } else {
05113          res = -1;
05114          ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
05115       }
05116       break;
05117    }
05118    case 77: /* M: Mute */ 
05119       user->adminflags |= ADMINFLAG_MUTED;
05120       break;
05121    case 78: /* N: Mute all (non-admin) users */
05122       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_muted_cb, &cnf);
05123       break;               
05124    case 109: /* m: Unmute */ 
05125       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
05126       break;
05127    case 110: /* n: Unmute all users */
05128       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, NULL);
05129       break;
05130    case 107: /* k: Kick user */ 
05131       user->adminflags |= ADMINFLAG_KICKME;
05132       break;
05133    case 118: /* v: Lower all users listen volume */
05134       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_voldown_cb, NULL);
05135       break;
05136    case 86: /* V: Raise all users listen volume */
05137       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_volup_cb, NULL);
05138       break;
05139    case 115: /* s: Lower all users speaking volume */
05140       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_voldown_cb, NULL);
05141       break;
05142    case 83: /* S: Raise all users speaking volume */
05143       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_volup_cb, NULL);
05144       break;
05145    case 82: /* R: Reset all volume levels */
05146       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_reset_vol_cb, NULL);
05147       break;
05148    case 114: /* r: Reset user's volume level */
05149       reset_volumes(user);
05150       break;
05151    case 85: /* U: Raise user's listen volume */
05152       tweak_listen_volume(user, VOL_UP);
05153       break;
05154    case 117: /* u: Lower user's listen volume */
05155       tweak_listen_volume(user, VOL_DOWN);
05156       break;
05157    case 84: /* T: Raise user's talk volume */
05158       tweak_talk_volume(user, VOL_UP);
05159       break;
05160    case 116: /* t: Lower user's talk volume */
05161       tweak_talk_volume(user, VOL_DOWN);
05162       break;
05163    case 'E': /* E: Extend conference */
05164       if (rt_extend_conf(args.confno)) {
05165          res = -1;
05166       }
05167       break;
05168    }
05169 
05170    if (args.user) {
05171       /* decrement reference from find_user */
05172       ao2_ref(user, -1);
05173    }
05174 usernotfound:
05175    AST_LIST_UNLOCK(&confs);
05176 
05177    dispose_conf(cnf);
05178    pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", res == -2 ? "NOTFOUND" : res ? "FAILED" : "OK");
05179 
05180    return 0;
05181 }
05182 
05183 /*! \brief The MeetMeChannelAdmin application 
05184    MeetMeChannelAdmin(channel, command) */
05185 static int channel_admin_exec(struct ast_channel *chan, const char *data) {
05186    char *params;
05187    struct ast_conference *conf = NULL;
05188    struct ast_conf_user *user = NULL;
05189    AST_DECLARE_APP_ARGS(args,
05190       AST_APP_ARG(channel);
05191       AST_APP_ARG(command);
05192    );
05193 
05194    if (ast_strlen_zero(data)) {
05195       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
05196       return -1;
05197    }
05198    
05199    params = ast_strdupa(data);
05200    AST_STANDARD_APP_ARGS(args, params);
05201 
05202    if (!args.channel) {
05203       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
05204       return -1;
05205    }
05206 
05207    if (!args.command) {
05208       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
05209       return -1;
05210    }
05211 
05212    AST_LIST_LOCK(&confs);
05213    AST_LIST_TRAVERSE(&confs, conf, list) {
05214       if ((user = ao2_callback(conf->usercontainer, 0, user_chan_cb, args.channel))) {
05215          break;
05216       }
05217    }
05218    
05219    if (!user) {
05220       ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
05221       AST_LIST_UNLOCK(&confs);
05222       return 0;
05223    }
05224    
05225    /* perform the specified action */
05226    switch (*args.command) {
05227       case 77: /* M: Mute */ 
05228          user->adminflags |= ADMINFLAG_MUTED;
05229          break;
05230       case 109: /* m: Unmute */ 
05231          user->adminflags &= ~ADMINFLAG_MUTED;
05232          break;
05233       case 107: /* k: Kick user */ 
05234          user->adminflags |= ADMINFLAG_KICKME;
05235          break;
05236       default: /* unknown command */
05237          ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
05238          break;
05239    }
05240    ao2_ref(user, -1);
05241    AST_LIST_UNLOCK(&confs);
05242    
05243    return 0;
05244 }
05245 
05246 static int meetmemute(struct mansession *s, const struct message *m, int mute)
05247 {
05248    struct ast_conference *conf;
05249    struct ast_conf_user *user;
05250    const char *confid = astman_get_header(m, "Meetme");
05251    char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
05252    int userno;
05253 
05254    if (ast_strlen_zero(confid)) {
05255       astman_send_error(s, m, "Meetme conference not specified");
05256       return 0;
05257    }
05258 
05259    if (ast_strlen_zero(userid)) {
05260       astman_send_error(s, m, "Meetme user number not specified");
05261       return 0;
05262    }
05263 
05264    userno = strtoul(userid, &userid, 10);
05265 
05266    if (*userid) {
05267       astman_send_error(s, m, "Invalid user number");
05268       return 0;
05269    }
05270 
05271    /* Look in the conference list */
05272    AST_LIST_LOCK(&confs);
05273    AST_LIST_TRAVERSE(&confs, conf, list) {
05274       if (!strcmp(confid, conf->confno))
05275          break;
05276    }
05277 
05278    if (!conf) {
05279       AST_LIST_UNLOCK(&confs);
05280       astman_send_error(s, m, "Meetme conference does not exist");
05281       return 0;
05282    }
05283 
05284    user = ao2_find(conf->usercontainer, &userno, 0);
05285 
05286    if (!user) {
05287       AST_LIST_UNLOCK(&confs);
05288       astman_send_error(s, m, "User number not found");
05289       return 0;
05290    }
05291 
05292    if (mute)
05293       user->adminflags |= ADMINFLAG_MUTED;   /* request user muting */
05294    else
05295       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST); /* request user unmuting */
05296 
05297    AST_LIST_UNLOCK(&confs);
05298 
05299    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));
05300 
05301    ao2_ref(user, -1);
05302    astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
05303    return 0;
05304 }
05305 
05306 static int action_meetmemute(struct mansession *s, const struct message *m)
05307 {
05308    return meetmemute(s, m, 1);
05309 }
05310 
05311 static int action_meetmeunmute(struct mansession *s, const struct message *m)
05312 {
05313    return meetmemute(s, m, 0);
05314 }
05315 
05316 static int action_meetmelist(struct mansession *s, const struct message *m)
05317 {
05318    const char *actionid = astman_get_header(m, "ActionID");
05319    const char *conference = astman_get_header(m, "Conference");
05320    char idText[80] = "";
05321    struct ast_conference *cnf;
05322    struct ast_conf_user *user;
05323    struct ao2_iterator user_iter;
05324    int total = 0;
05325 
05326    if (!ast_strlen_zero(actionid))
05327       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
05328 
05329    if (AST_LIST_EMPTY(&confs)) {
05330       astman_send_error(s, m, "No active conferences.");
05331       return 0;
05332    }
05333 
05334    astman_send_listack(s, m, "Meetme user list will follow", "start");
05335 
05336    /* Find the right conference */
05337    AST_LIST_LOCK(&confs);
05338    AST_LIST_TRAVERSE(&confs, cnf, list) {
05339       /* If we ask for one particular, and this isn't it, skip it */
05340       if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
05341          continue;
05342 
05343       /* Show all the users */
05344       user_iter = ao2_iterator_init(cnf->usercontainer, 0);
05345       while ((user = ao2_iterator_next(&user_iter))) {
05346          total++;
05347          astman_append(s,
05348             "Event: MeetmeList\r\n"
05349             "%s"
05350             "Conference: %s\r\n"
05351             "UserNumber: %d\r\n"
05352             "CallerIDNum: %s\r\n"
05353             "CallerIDName: %s\r\n"
05354             "ConnectedLineNum: %s\r\n"
05355             "ConnectedLineName: %s\r\n"
05356             "Channel: %s\r\n"
05357             "Admin: %s\r\n"
05358             "Role: %s\r\n"
05359             "MarkedUser: %s\r\n"
05360             "Muted: %s\r\n"
05361             "Talking: %s\r\n"
05362             "\r\n",
05363             idText,
05364             cnf->confno,
05365             user->user_no,
05366             S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
05367             S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
05368             S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, "<unknown>"),
05369             S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "<no name>"),
05370             ast_channel_name(user->chan),
05371             ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "Yes" : "No",
05372             ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "Listen only" : ast_test_flag64(&user->userflags, CONFFLAG_TALKER) ? "Talk only" : "Talk and listen",
05373             ast_test_flag64(&user->userflags, CONFFLAG_MARKEDUSER) ? "Yes" : "No",
05374             user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
05375             user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
05376          ao2_ref(user, -1);
05377       }
05378       ao2_iterator_destroy(&user_iter);
05379    }
05380    AST_LIST_UNLOCK(&confs);
05381    /* Send final confirmation */
05382    astman_append(s,
05383    "Event: MeetmeListComplete\r\n"
05384    "EventList: Complete\r\n"
05385    "ListItems: %d\r\n"
05386    "%s"
05387    "\r\n", total, idText);
05388    return 0;
05389 }
05390 
05391 static int action_meetmelistrooms(struct mansession *s, const struct message *m)
05392 {
05393    const char *actionid = astman_get_header(m, "ActionID");
05394    char idText[80] = "";
05395    struct ast_conference *cnf;
05396    int totalitems = 0;
05397    int hr, min, sec;
05398    time_t now;
05399    char markedusers[5];
05400 
05401    if (!ast_strlen_zero(actionid)) {
05402       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
05403    }
05404 
05405    if (AST_LIST_EMPTY(&confs)) {
05406       astman_send_error(s, m, "No active conferences.");
05407       return 0;
05408    }
05409 
05410    astman_send_listack(s, m, "Meetme conferences will follow", "start");
05411 
05412    now = time(NULL);
05413 
05414    /* Traverse the conference list */
05415    AST_LIST_LOCK(&confs);
05416    AST_LIST_TRAVERSE(&confs, cnf, list) {
05417       totalitems++;
05418 
05419       if (cnf->markedusers == 0) {
05420          strcpy(markedusers, "N/A");
05421       } else {
05422          sprintf(markedusers, "%.4d", cnf->markedusers);
05423       }
05424       hr = (now - cnf->start) / 3600;
05425       min = ((now - cnf->start) % 3600) / 60;
05426       sec = (now - cnf->start) % 60;
05427 
05428       astman_append(s,
05429       "Event: MeetmeListRooms\r\n"
05430       "%s"
05431       "Conference: %s\r\n"
05432       "Parties: %d\r\n"
05433       "Marked: %s\r\n"
05434       "Activity: %2.2d:%2.2d:%2.2d\r\n"
05435       "Creation: %s\r\n"
05436       "Locked: %s\r\n"
05437       "\r\n",
05438       idText,
05439       cnf->confno,
05440       cnf->users,
05441       markedusers,
05442       hr,  min, sec,
05443       cnf->isdynamic ? "Dynamic" : "Static",
05444       cnf->locked ? "Yes" : "No"); 
05445    }
05446    AST_LIST_UNLOCK(&confs);
05447 
05448    /* Send final confirmation */
05449    astman_append(s,
05450    "Event: MeetmeListRoomsComplete\r\n"
05451    "EventList: Complete\r\n"
05452    "ListItems: %d\r\n"
05453    "%s"
05454    "\r\n", totalitems, idText);
05455    return 0;
05456 }
05457 
05458 /*! \internal
05459  * \brief creates directory structure and assigns absolute path from relative paths for filenames
05460  *
05461  * \param filename contains the absolute or relative path to the desired file
05462  * \param buffer stores completed filename, absolutely must be a buffer of PATH_MAX length
05463  */
05464 static void filename_parse(char *filename, char *buffer)
05465 {
05466    char *slash;
05467    if (ast_strlen_zero(filename)) {
05468       ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
05469    } else if (filename[0] != '/') {
05470       snprintf(buffer, PATH_MAX, "%s/meetme/%s", ast_config_AST_SPOOL_DIR, filename);
05471    } else {
05472       ast_copy_string(buffer, filename, PATH_MAX);
05473    }
05474 
05475    slash = buffer;
05476    if ((slash = strrchr(slash, '/'))) {
05477       *slash = '\0';
05478       ast_mkdir(buffer, 0777);
05479       *slash = '/';
05480    }
05481 }
05482 
05483 static void *recordthread(void *args)
05484 {
05485    struct ast_conference *cnf = args;
05486    struct ast_frame *f = NULL;
05487    int flags;
05488    struct ast_filestream *s = NULL;
05489    int res = 0;
05490    int x;
05491    const char *oldrecordingfilename = NULL;
05492    char filename_buffer[PATH_MAX];
05493 
05494    if (!cnf || !cnf->lchan) {
05495       pthread_exit(0);
05496    }
05497 
05498    filename_buffer[0] = '\0';
05499    filename_parse(cnf->recordingfilename, filename_buffer);
05500 
05501    ast_stopstream(cnf->lchan);
05502    flags = O_CREAT | O_TRUNC | O_WRONLY;
05503 
05504 
05505    cnf->recording = MEETME_RECORD_ACTIVE;
05506    while (ast_waitfor(cnf->lchan, -1) > -1) {
05507       if (cnf->recording == MEETME_RECORD_TERMINATE) {
05508          AST_LIST_LOCK(&confs);
05509          AST_LIST_UNLOCK(&confs);
05510          break;
05511       }
05512       if (!s && !(ast_strlen_zero(filename_buffer)) && (filename_buffer != oldrecordingfilename)) {
05513          s = ast_writefile(filename_buffer, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
05514          oldrecordingfilename = filename_buffer;
05515       }
05516       
05517       f = ast_read(cnf->lchan);
05518       if (!f) {
05519          res = -1;
05520          break;
05521       }
05522       if (f->frametype == AST_FRAME_VOICE) {
05523          ast_mutex_lock(&cnf->listenlock);
05524          for (x = 0; x < AST_FRAME_BITS; x++) {
05525             /* Free any translations that have occured */
05526             if (cnf->transframe[x]) {
05527                ast_frfree(cnf->transframe[x]);
05528                cnf->transframe[x] = NULL;
05529             }
05530          }
05531          if (cnf->origframe)
05532             ast_frfree(cnf->origframe);
05533          cnf->origframe = ast_frdup(f);
05534          ast_mutex_unlock(&cnf->listenlock);
05535          if (s)
05536             res = ast_writestream(s, f);
05537          if (res) {
05538             ast_frfree(f);
05539             break;
05540          }
05541       }
05542       ast_frfree(f);
05543    }
05544    cnf->recording = MEETME_RECORD_OFF;
05545    if (s)
05546       ast_closestream(s);
05547    
05548    pthread_exit(0);
05549 }
05550 
05551 /*! \brief Callback for devicestate providers */
05552 static enum ast_device_state meetmestate(const char *data)
05553 {
05554    struct ast_conference *conf;
05555 
05556    /* Find conference */
05557    AST_LIST_LOCK(&confs);
05558    AST_LIST_TRAVERSE(&confs, conf, list) {
05559       if (!strcmp(data, conf->confno))
05560          break;
05561    }
05562    AST_LIST_UNLOCK(&confs);
05563    if (!conf)
05564       return AST_DEVICE_INVALID;
05565 
05566 
05567    /* SKREP to fill */
05568    if (!conf->users)
05569       return AST_DEVICE_NOT_INUSE;
05570 
05571    return AST_DEVICE_INUSE;
05572 }
05573 
05574 static void load_config_meetme(void)
05575 {
05576    struct ast_config *cfg;
05577    struct ast_flags config_flags = { 0 };
05578    const char *val;
05579 
05580    if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags))) {
05581       return;
05582    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
05583       ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format.  Aborting.\n");
05584       return;
05585    }
05586 
05587    audio_buffers = DEFAULT_AUDIO_BUFFERS;
05588 
05589    /*  Scheduling support is off by default */
05590    rt_schedule = 0;
05591    fuzzystart = 0;
05592    earlyalert = 0;
05593    endalert = 0;
05594    extendby = 0;
05595 
05596    /*  Logging of participants defaults to ON for compatibility reasons */
05597    rt_log_members = 1;  
05598 
05599    if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
05600       if ((sscanf(val, "%30d", &audio_buffers) != 1)) {
05601          ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
05602          audio_buffers = DEFAULT_AUDIO_BUFFERS;
05603       } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
05604          ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
05605             DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
05606          audio_buffers = DEFAULT_AUDIO_BUFFERS;
05607       }
05608       if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
05609          ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
05610    }
05611 
05612    if ((val = ast_variable_retrieve(cfg, "general", "schedule")))
05613       rt_schedule = ast_true(val);
05614    if ((val = ast_variable_retrieve(cfg, "general", "logmembercount")))
05615       rt_log_members = ast_true(val);
05616    if ((val = ast_variable_retrieve(cfg, "general", "fuzzystart"))) {
05617       if ((sscanf(val, "%30d", &fuzzystart) != 1)) {
05618          ast_log(LOG_WARNING, "fuzzystart must be a number, not '%s'\n", val);
05619          fuzzystart = 0;
05620       } 
05621    }
05622    if ((val = ast_variable_retrieve(cfg, "general", "earlyalert"))) {
05623       if ((sscanf(val, "%30d", &earlyalert) != 1)) {
05624          ast_log(LOG_WARNING, "earlyalert must be a number, not '%s'\n", val);
05625          earlyalert = 0;
05626       } 
05627    }
05628    if ((val = ast_variable_retrieve(cfg, "general", "endalert"))) {
05629       if ((sscanf(val, "%30d", &endalert) != 1)) {
05630          ast_log(LOG_WARNING, "endalert must be a number, not '%s'\n", val);
05631          endalert = 0;
05632       } 
05633    }
05634    if ((val = ast_variable_retrieve(cfg, "general", "extendby"))) {
05635       if ((sscanf(val, "%30d", &extendby) != 1)) {
05636          ast_log(LOG_WARNING, "extendby must be a number, not '%s'\n", val);
05637          extendby = 0;
05638       } 
05639    }
05640 
05641    ast_config_destroy(cfg);
05642 }
05643 
05644 /*!
05645  * \internal
05646  * \brief Find an SLA trunk by name
05647  */
05648 static struct sla_trunk *sla_find_trunk(const char *name)
05649 {
05650    struct sla_trunk tmp_trunk = {
05651       .name = name,
05652    };
05653 
05654    return ao2_find(sla_trunks, &tmp_trunk, OBJ_POINTER);
05655 }
05656 
05657 /*!
05658  * \internal
05659  * \brief Find an SLA station by name
05660  */
05661 static struct sla_station *sla_find_station(const char *name)
05662 {
05663    struct sla_station tmp_station = {
05664       .name = name,
05665    };
05666 
05667    return ao2_find(sla_stations, &tmp_station, OBJ_POINTER);
05668 }
05669 
05670 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
05671    const struct sla_station *station)
05672 {
05673    struct sla_station_ref *station_ref;
05674    struct sla_trunk_ref *trunk_ref;
05675 
05676    /* For each station that has this call on hold, check for private hold. */
05677    AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
05678       AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
05679          if (trunk_ref->trunk != trunk || station_ref->station == station)
05680             continue;
05681          if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
05682             station_ref->station->hold_access == SLA_HOLD_PRIVATE)
05683             return 1;
05684          return 0;
05685       }
05686    }
05687 
05688    return 0;
05689 }
05690 
05691 /*!
05692  * \brief Find a trunk reference on a station by name
05693  * \param station the station
05694  * \param name the trunk's name
05695  * \pre sla_station is locked
05696  * \return a pointer to the station's trunk reference.  If the trunk
05697  *         is not found, it is not idle and barge is disabled, or if
05698  *         it is on hold and private hold is set, then NULL will be returned.
05699  */
05700 static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
05701    const char *name)
05702 {
05703    struct sla_trunk_ref *trunk_ref = NULL;
05704 
05705    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05706       if (strcasecmp(trunk_ref->trunk->name, name))
05707          continue;
05708 
05709       if ( (trunk_ref->trunk->barge_disabled 
05710          && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
05711          (trunk_ref->trunk->hold_stations 
05712          && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
05713          && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
05714          sla_check_station_hold_access(trunk_ref->trunk, station) ) 
05715       {
05716          trunk_ref = NULL;
05717       }
05718 
05719       break;
05720    }
05721 
05722    if (trunk_ref) {
05723       ao2_ref(trunk_ref, 1);
05724    }
05725 
05726    return trunk_ref;
05727 }
05728 
05729 static void sla_station_ref_destructor(void *obj)
05730 {
05731    struct sla_station_ref *station_ref = obj;
05732 
05733    if (station_ref->station) {
05734       ao2_ref(station_ref->station, -1);
05735       station_ref->station = NULL;
05736    }
05737 }
05738 
05739 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
05740 {
05741    struct sla_station_ref *station_ref;
05742 
05743    if (!(station_ref = ao2_alloc(sizeof(*station_ref), sla_station_ref_destructor))) {
05744       return NULL;
05745    }
05746 
05747    ao2_ref(station, 1);
05748    station_ref->station = station;
05749 
05750    return station_ref;
05751 }
05752 
05753 static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
05754 {
05755    struct sla_ringing_station *ringing_station;
05756 
05757    if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
05758       return NULL;
05759 
05760    ao2_ref(station, 1);
05761    ringing_station->station = station;
05762    ringing_station->ring_begin = ast_tvnow();
05763 
05764    return ringing_station;
05765 }
05766 
05767 static void sla_ringing_station_destroy(struct sla_ringing_station *ringing_station)
05768 {
05769    if (ringing_station->station) {
05770       ao2_ref(ringing_station->station, -1);
05771       ringing_station->station = NULL;
05772    }
05773 
05774    ast_free(ringing_station);
05775 }
05776 
05777 static struct sla_failed_station *sla_create_failed_station(struct sla_station *station)
05778 {
05779    struct sla_failed_station *failed_station;
05780 
05781    if (!(failed_station = ast_calloc(1, sizeof(*failed_station)))) {
05782       return NULL;
05783    }
05784 
05785    ao2_ref(station, 1);
05786    failed_station->station = station;
05787    failed_station->last_try = ast_tvnow();
05788 
05789    return failed_station;
05790 }
05791 
05792 static void sla_failed_station_destroy(struct sla_failed_station *failed_station)
05793 {
05794    if (failed_station->station) {
05795       ao2_ref(failed_station->station, -1);
05796       failed_station->station = NULL;
05797    }
05798 
05799    ast_free(failed_station);
05800 }
05801 
05802 static enum ast_device_state sla_state_to_devstate(enum sla_trunk_state state)
05803 {
05804    switch (state) {
05805    case SLA_TRUNK_STATE_IDLE:
05806       return AST_DEVICE_NOT_INUSE;
05807    case SLA_TRUNK_STATE_RINGING:
05808       return AST_DEVICE_RINGING;
05809    case SLA_TRUNK_STATE_UP:
05810       return AST_DEVICE_INUSE;
05811    case SLA_TRUNK_STATE_ONHOLD:
05812    case SLA_TRUNK_STATE_ONHOLD_BYME:
05813       return AST_DEVICE_ONHOLD;
05814    }
05815 
05816    return AST_DEVICE_UNKNOWN;
05817 }
05818 
05819 static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state, 
05820    enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
05821 {
05822    struct sla_station *station;
05823    struct sla_trunk_ref *trunk_ref;
05824    struct ao2_iterator i;
05825 
05826    i = ao2_iterator_init(sla_stations, 0);
05827    while ((station = ao2_iterator_next(&i))) {
05828       ao2_lock(station);
05829       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05830          if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
05831                || trunk_ref == exclude) {
05832             continue;
05833          }
05834          trunk_ref->state = state;
05835          ast_devstate_changed(sla_state_to_devstate(state), AST_DEVSTATE_CACHABLE,
05836                     "SLA:%s_%s", station->name, trunk->name);
05837          break;
05838       }
05839       ao2_unlock(station);
05840       ao2_ref(station, -1);
05841    }
05842    ao2_iterator_destroy(&i);
05843 }
05844 
05845 struct run_station_args {
05846    struct sla_station *station;
05847    struct sla_trunk_ref *trunk_ref;
05848    ast_mutex_t *cond_lock;
05849    ast_cond_t *cond;
05850 };
05851 
05852 static void answer_trunk_chan(struct ast_channel *chan)
05853 {
05854    ast_answer(chan);
05855    ast_indicate(chan, -1);
05856 }
05857 
05858 static void *run_station(void *data)
05859 {
05860    RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
05861    RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
05862    struct ast_str *conf_name = ast_str_create(16);
05863    struct ast_flags64 conf_flags = { 0 };
05864    struct ast_conference *conf;
05865 
05866    {
05867       struct run_station_args *args = data;
05868       station = args->station;
05869       trunk_ref = args->trunk_ref;
05870       ast_mutex_lock(args->cond_lock);
05871       ast_cond_signal(args->cond);
05872       ast_mutex_unlock(args->cond_lock);
05873       /* args is no longer valid here. */
05874    }
05875 
05876    ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
05877    ast_str_set(&conf_name, 0, "SLA_%s", trunk_ref->trunk->name);
05878    ast_set_flag64(&conf_flags, 
05879       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
05880    answer_trunk_chan(trunk_ref->chan);
05881    conf = build_conf(ast_str_buffer(conf_name), "", "", 0, 0, 1, trunk_ref->chan, NULL);
05882    if (conf) {
05883       conf_run(trunk_ref->chan, conf, &conf_flags, NULL);
05884       dispose_conf(conf);
05885       conf = NULL;
05886    }
05887    trunk_ref->chan = NULL;
05888    if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
05889       trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
05890       ast_str_append(&conf_name, 0, ",K");
05891       admin_exec(NULL, ast_str_buffer(conf_name));
05892       trunk_ref->trunk->hold_stations = 0;
05893       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05894    }
05895 
05896    ast_dial_join(station->dial);
05897    ast_dial_destroy(station->dial);
05898    station->dial = NULL;
05899    ast_free(conf_name);
05900 
05901    return NULL;
05902 }
05903 
05904 static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk);
05905 
05906 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
05907 {
05908    char buf[80];
05909    struct sla_station_ref *station_ref;
05910 
05911    snprintf(buf, sizeof(buf), "SLA_%s,K", ringing_trunk->trunk->name);
05912    admin_exec(NULL, buf);
05913    sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05914 
05915    while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry))) {
05916       ao2_ref(station_ref, -1);
05917    }
05918 
05919    sla_ringing_trunk_destroy(ringing_trunk);
05920 }
05921 
05922 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
05923    enum sla_station_hangup hangup)
05924 {
05925    struct sla_ringing_trunk *ringing_trunk;
05926    struct sla_trunk_ref *trunk_ref;
05927    struct sla_station_ref *station_ref;
05928 
05929    ast_dial_join(ringing_station->station->dial);
05930    ast_dial_destroy(ringing_station->station->dial);
05931    ringing_station->station->dial = NULL;
05932 
05933    if (hangup == SLA_STATION_HANGUP_NORMAL)
05934       goto done;
05935 
05936    /* If the station is being hung up because of a timeout, then add it to the
05937     * list of timed out stations on each of the ringing trunks.  This is so
05938     * that when doing further processing to figure out which stations should be
05939     * ringing, which trunk to answer, determining timeouts, etc., we know which
05940     * ringing trunks we should ignore. */
05941    AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05942       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
05943          if (ringing_trunk->trunk == trunk_ref->trunk)
05944             break;
05945       }
05946       if (!trunk_ref)
05947          continue;
05948       if (!(station_ref = sla_create_station_ref(ringing_station->station)))
05949          continue;
05950       AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
05951    }
05952 
05953 done:
05954    sla_ringing_station_destroy(ringing_station);
05955 }
05956 
05957 static void sla_dial_state_callback(struct ast_dial *dial)
05958 {
05959    sla_queue_event(SLA_EVENT_DIAL_STATE);
05960 }
05961 
05962 /*! \brief Check to see if dialing this station already timed out for this ringing trunk
05963  * \note Assumes sla.lock is locked
05964  */
05965 static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
05966    const struct sla_station *station)
05967 {
05968    struct sla_station_ref *timed_out_station;
05969 
05970    AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
05971       if (station == timed_out_station->station)
05972          return 1;
05973    }
05974 
05975    return 0;
05976 }
05977 
05978 /*! \brief Choose the highest priority ringing trunk for a station
05979  * \param station the station
05980  * \param rm remove the ringing trunk once selected
05981  * \param trunk_ref a place to store the pointer to this stations reference to
05982  *        the selected trunk
05983  * \return a pointer to the selected ringing trunk, or NULL if none found
05984  * \note Assumes that sla.lock is locked
05985  */
05986 static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station, 
05987    struct sla_trunk_ref **trunk_ref, int rm)
05988 {
05989    struct sla_trunk_ref *s_trunk_ref;
05990    struct sla_ringing_trunk *ringing_trunk = NULL;
05991 
05992    AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
05993       AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
05994          /* Make sure this is the trunk we're looking for */
05995          if (s_trunk_ref->trunk != ringing_trunk->trunk)
05996             continue;
05997 
05998          /* This trunk on the station is ringing.  But, make sure this station
05999           * didn't already time out while this trunk was ringing. */
06000          if (sla_check_timed_out_station(ringing_trunk, station))
06001             continue;
06002 
06003          if (rm)
06004             AST_LIST_REMOVE_CURRENT(entry);
06005 
06006          if (trunk_ref) {
06007             ao2_ref(s_trunk_ref, 1);
06008             *trunk_ref = s_trunk_ref;
06009          }
06010 
06011          break;
06012       }
06013       AST_LIST_TRAVERSE_SAFE_END;
06014    
06015       if (ringing_trunk)
06016          break;
06017    }
06018 
06019    return ringing_trunk;
06020 }
06021 
06022 static void sla_handle_dial_state_event(void)
06023 {
06024    struct sla_ringing_station *ringing_station;
06025 
06026    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
06027       RAII_VAR(struct sla_trunk_ref *, s_trunk_ref, NULL, ao2_cleanup);
06028       struct sla_ringing_trunk *ringing_trunk = NULL;
06029       struct run_station_args args;
06030       enum ast_dial_result dial_res;
06031       pthread_t dont_care;
06032       ast_mutex_t cond_lock;
06033       ast_cond_t cond;
06034 
06035       switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
06036       case AST_DIAL_RESULT_HANGUP:
06037       case AST_DIAL_RESULT_INVALID:
06038       case AST_DIAL_RESULT_FAILED:
06039       case AST_DIAL_RESULT_TIMEOUT:
06040       case AST_DIAL_RESULT_UNANSWERED:
06041          AST_LIST_REMOVE_CURRENT(entry);
06042          sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
06043          break;
06044       case AST_DIAL_RESULT_ANSWERED:
06045          AST_LIST_REMOVE_CURRENT(entry);
06046          /* Find the appropriate trunk to answer. */
06047          ast_mutex_lock(&sla.lock);
06048          ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
06049          ast_mutex_unlock(&sla.lock);
06050          if (!ringing_trunk) {
06051             /* This case happens in a bit of a race condition.  If two stations answer
06052              * the outbound call at the same time, the first one will get connected to
06053              * the trunk.  When the second one gets here, it will not see any trunks
06054              * ringing so we have no idea what to conect it to.  So, we just hang up
06055              * on it. */
06056             ast_debug(1, "Found no ringing trunk for station '%s' to answer!\n", ringing_station->station->name);
06057             ast_dial_join(ringing_station->station->dial);
06058             ast_dial_destroy(ringing_station->station->dial);
06059             ringing_station->station->dial = NULL;
06060             sla_ringing_station_destroy(ringing_station);
06061             break;
06062          }
06063          /* Track the channel that answered this trunk */
06064          s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
06065          /* Actually answer the trunk */
06066          answer_trunk_chan(ringing_trunk->trunk->chan);
06067          sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06068          /* Now, start a thread that will connect this station to the trunk.  The rest of
06069           * the code here sets up the thread and ensures that it is able to save the arguments
06070           * before they are no longer valid since they are allocated on the stack. */
06071          ao2_ref(s_trunk_ref, 1);
06072          args.trunk_ref = s_trunk_ref;
06073          ao2_ref(ringing_station->station, 1);
06074          args.station = ringing_station->station;
06075          args.cond = &cond;
06076          args.cond_lock = &cond_lock;
06077          sla_ringing_trunk_destroy(ringing_trunk);
06078          sla_ringing_station_destroy(ringing_station);
06079          ast_mutex_init(&cond_lock);
06080          ast_cond_init(&cond, NULL);
06081          ast_mutex_lock(&cond_lock);
06082          ast_pthread_create_detached_background(&dont_care, NULL, run_station, &args);
06083          ast_cond_wait(&cond, &cond_lock);
06084          ast_mutex_unlock(&cond_lock);
06085          ast_mutex_destroy(&cond_lock);
06086          ast_cond_destroy(&cond);
06087          break;
06088       case AST_DIAL_RESULT_TRYING:
06089       case AST_DIAL_RESULT_RINGING:
06090       case AST_DIAL_RESULT_PROGRESS:
06091       case AST_DIAL_RESULT_PROCEEDING:
06092          break;
06093       }
06094       if (dial_res == AST_DIAL_RESULT_ANSWERED) {
06095          /* Queue up reprocessing ringing trunks, and then ringing stations again */
06096          sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06097          sla_queue_event(SLA_EVENT_DIAL_STATE);
06098          break;
06099       }
06100    }
06101    AST_LIST_TRAVERSE_SAFE_END;
06102 }
06103 
06104 /*! \brief Check to see if this station is already ringing 
06105  * \note Assumes sla.lock is locked 
06106  */
06107 static int sla_check_ringing_station(const struct sla_station *station)
06108 {
06109    struct sla_ringing_station *ringing_station;
06110 
06111    AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
06112       if (station == ringing_station->station)
06113          return 1;
06114    }
06115 
06116    return 0;
06117 }
06118 
06119 /*! \brief Check to see if this station has failed to be dialed in the past minute
06120  * \note assumes sla.lock is locked
06121  */
06122 static int sla_check_failed_station(const struct sla_station *station)
06123 {
06124    struct sla_failed_station *failed_station;
06125    int res = 0;
06126 
06127    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
06128       if (station != failed_station->station)
06129          continue;
06130       if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
06131          AST_LIST_REMOVE_CURRENT(entry);
06132          sla_failed_station_destroy(failed_station);
06133          break;
06134       }
06135       res = 1;
06136    }
06137    AST_LIST_TRAVERSE_SAFE_END
06138 
06139    return res;
06140 }
06141 
06142 /*! \brief Ring a station
06143  * \note Assumes sla.lock is locked
06144  */
06145 static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
06146 {
06147    char *tech, *tech_data;
06148    struct ast_dial *dial;
06149    struct sla_ringing_station *ringing_station;
06150    enum ast_dial_result res;
06151    int caller_is_saved;
06152    struct ast_party_caller caller;
06153 
06154    if (!(dial = ast_dial_create()))
06155       return -1;
06156 
06157    ast_dial_set_state_callback(dial, sla_dial_state_callback);
06158    tech_data = ast_strdupa(station->device);
06159    tech = strsep(&tech_data, "/");
06160 
06161    if (ast_dial_append(dial, tech, tech_data) == -1) {
06162       ast_dial_destroy(dial);
06163       return -1;
06164    }
06165 
06166    /* Do we need to save off the caller ID data? */
06167    caller_is_saved = 0;
06168    if (!sla.attempt_callerid) {
06169       caller_is_saved = 1;
06170       caller = *ast_channel_caller(ringing_trunk->trunk->chan);
06171       ast_party_caller_init(ast_channel_caller(ringing_trunk->trunk->chan));
06172    }
06173 
06174    res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
06175    
06176    /* Restore saved caller ID */
06177    if (caller_is_saved) {
06178       ast_party_caller_free(ast_channel_caller(ringing_trunk->trunk->chan));
06179       ast_channel_caller_set(ringing_trunk->trunk->chan, &caller);
06180    }
06181    
06182    if (res != AST_DIAL_RESULT_TRYING) {
06183       struct sla_failed_station *failed_station;
06184       ast_dial_destroy(dial);
06185       if ((failed_station = sla_create_failed_station(station))) {
06186          AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
06187       }
06188       return -1;
06189    }
06190    if (!(ringing_station = sla_create_ringing_station(station))) {
06191       ast_dial_join(dial);
06192       ast_dial_destroy(dial);
06193       return -1;
06194    }
06195 
06196    station->dial = dial;
06197 
06198    AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
06199 
06200    return 0;
06201 }
06202 
06203 /*! \brief Check to see if a station is in use
06204  */
06205 static int sla_check_inuse_station(const struct sla_station *station)
06206 {
06207    struct sla_trunk_ref *trunk_ref;
06208 
06209    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06210       if (trunk_ref->chan)
06211          return 1;
06212    }
06213 
06214    return 0;
06215 }
06216 
06217 static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
06218    const struct sla_trunk *trunk)
06219 {
06220    struct sla_trunk_ref *trunk_ref = NULL;
06221 
06222    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06223       if (trunk_ref->trunk == trunk)
06224          break;
06225    }
06226 
06227    ao2_ref(trunk_ref, 1);
06228 
06229    return trunk_ref;
06230 }
06231 
06232 /*! \brief Calculate the ring delay for a given ringing trunk on a station
06233  * \param station the station
06234  * \param ringing_trunk the trunk.  If NULL, the highest priority ringing trunk will be used
06235  * \return the number of ms left before the delay is complete, or INT_MAX if there is no delay
06236  */
06237 static int sla_check_station_delay(struct sla_station *station, 
06238    struct sla_ringing_trunk *ringing_trunk)
06239 {
06240    RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
06241    unsigned int delay = UINT_MAX;
06242    int time_left, time_elapsed;
06243 
06244    if (!ringing_trunk)
06245       ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
06246    else
06247       trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
06248 
06249    if (!ringing_trunk || !trunk_ref)
06250       return delay;
06251 
06252    /* If this station has a ring delay specific to the highest priority
06253     * ringing trunk, use that.  Otherwise, use the ring delay specified
06254     * globally for the station. */
06255    delay = trunk_ref->ring_delay;
06256    if (!delay)
06257       delay = station->ring_delay;
06258    if (!delay)
06259       return INT_MAX;
06260 
06261    time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
06262    time_left = (delay * 1000) - time_elapsed;
06263 
06264    return time_left;
06265 }
06266 
06267 /*! \brief Ring stations based on current set of ringing trunks
06268  * \note Assumes that sla.lock is locked
06269  */
06270 static void sla_ring_stations(void)
06271 {
06272    struct sla_station_ref *station_ref;
06273    struct sla_ringing_trunk *ringing_trunk;
06274 
06275    /* Make sure that every station that uses at least one of the ringing
06276     * trunks, is ringing. */
06277    AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
06278       AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
06279          int time_left;
06280 
06281          /* Is this station already ringing? */
06282          if (sla_check_ringing_station(station_ref->station))
06283             continue;
06284 
06285          /* Is this station already in a call? */
06286          if (sla_check_inuse_station(station_ref->station))
06287             continue;
06288 
06289          /* Did we fail to dial this station earlier?  If so, has it been
06290           * a minute since we tried? */
06291          if (sla_check_failed_station(station_ref->station))
06292             continue;
06293 
06294          /* If this station already timed out while this trunk was ringing,
06295           * do not dial it again for this ringing trunk. */
06296          if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
06297             continue;
06298 
06299          /* Check for a ring delay in progress */
06300          time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
06301          if (time_left != INT_MAX && time_left > 0)
06302             continue;
06303 
06304          /* It is time to make this station begin to ring.  Do it! */
06305          sla_ring_station(ringing_trunk, station_ref->station);
06306       }
06307    }
06308    /* Now, all of the stations that should be ringing, are ringing. */
06309 }
06310 
06311 static void sla_hangup_stations(void)
06312 {
06313    struct sla_trunk_ref *trunk_ref;
06314    struct sla_ringing_station *ringing_station;
06315 
06316    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
06317       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
06318          struct sla_ringing_trunk *ringing_trunk;
06319          ast_mutex_lock(&sla.lock);
06320          AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
06321             if (trunk_ref->trunk == ringing_trunk->trunk)
06322                break;
06323          }
06324          ast_mutex_unlock(&sla.lock);
06325          if (ringing_trunk)
06326             break;
06327       }
06328       if (!trunk_ref) {
06329          AST_LIST_REMOVE_CURRENT(entry);
06330          ast_dial_join(ringing_station->station->dial);
06331          ast_dial_destroy(ringing_station->station->dial);
06332          ringing_station->station->dial = NULL;
06333          sla_ringing_station_destroy(ringing_station);
06334       }
06335    }
06336    AST_LIST_TRAVERSE_SAFE_END
06337 }
06338 
06339 static void sla_handle_ringing_trunk_event(void)
06340 {
06341    ast_mutex_lock(&sla.lock);
06342    sla_ring_stations();
06343    ast_mutex_unlock(&sla.lock);
06344 
06345    /* Find stations that shouldn't be ringing anymore. */
06346    sla_hangup_stations();
06347 }
06348 
06349 static void sla_handle_hold_event(struct sla_event *event)
06350 {
06351    ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
06352    event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
06353    ast_devstate_changed(AST_DEVICE_ONHOLD, AST_DEVSTATE_CACHABLE, "SLA:%s_%s",
06354               event->station->name, event->trunk_ref->trunk->name);
06355    sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD, 
06356       INACTIVE_TRUNK_REFS, event->trunk_ref);
06357 
06358    if (event->trunk_ref->trunk->active_stations == 1) {
06359       /* The station putting it on hold is the only one on the call, so start
06360        * Music on hold to the trunk. */
06361       event->trunk_ref->trunk->on_hold = 1;
06362       ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
06363    }
06364 
06365    ast_softhangup(event->trunk_ref->chan, AST_SOFTHANGUP_DEV);
06366    event->trunk_ref->chan = NULL;
06367 }
06368 
06369 /*! \brief Process trunk ring timeouts
06370  * \note Called with sla.lock locked
06371  * \return non-zero if a change to the ringing trunks was made
06372  */
06373 static int sla_calc_trunk_timeouts(unsigned int *timeout)
06374 {
06375    struct sla_ringing_trunk *ringing_trunk;
06376    int res = 0;
06377 
06378    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06379       int time_left, time_elapsed;
06380       if (!ringing_trunk->trunk->ring_timeout)
06381          continue;
06382       time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
06383       time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
06384       if (time_left <= 0) {
06385          pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
06386          AST_LIST_REMOVE_CURRENT(entry);
06387          sla_stop_ringing_trunk(ringing_trunk);
06388          res = 1;
06389          continue;
06390       }
06391       if (time_left < *timeout)
06392          *timeout = time_left;
06393    }
06394    AST_LIST_TRAVERSE_SAFE_END;
06395 
06396    return res;
06397 }
06398 
06399 /*! \brief Process station ring timeouts
06400  * \note Called with sla.lock locked
06401  * \return non-zero if a change to the ringing stations was made
06402  */
06403 static int sla_calc_station_timeouts(unsigned int *timeout)
06404 {
06405    struct sla_ringing_trunk *ringing_trunk;
06406    struct sla_ringing_station *ringing_station;
06407    int res = 0;
06408 
06409    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
06410       unsigned int ring_timeout = 0;
06411       int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
06412       struct sla_trunk_ref *trunk_ref;
06413 
06414       /* If there are any ring timeouts specified for a specific trunk
06415        * on the station, then use the highest per-trunk ring timeout.
06416        * Otherwise, use the ring timeout set for the entire station. */
06417       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
06418          struct sla_station_ref *station_ref;
06419          int trunk_time_elapsed, trunk_time_left;
06420 
06421          AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
06422             if (ringing_trunk->trunk == trunk_ref->trunk)
06423                break;
06424          }
06425          if (!ringing_trunk)
06426             continue;
06427 
06428          /* If there is a trunk that is ringing without a timeout, then the
06429           * only timeout that could matter is a global station ring timeout. */
06430          if (!trunk_ref->ring_timeout)
06431             break;
06432 
06433          /* This trunk on this station is ringing and has a timeout.
06434           * However, make sure this trunk isn't still ringing from a
06435           * previous timeout.  If so, don't consider it. */
06436          AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
06437             if (station_ref->station == ringing_station->station)
06438                break;
06439          }
06440          if (station_ref)
06441             continue;
06442 
06443          trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
06444          trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
06445          if (trunk_time_left > final_trunk_time_left)
06446             final_trunk_time_left = trunk_time_left;
06447       }
06448 
06449       /* No timeout was found for ringing trunks, and no timeout for the entire station */
06450       if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
06451          continue;
06452 
06453       /* Compute how much time is left for a global station timeout */
06454       if (ringing_station->station->ring_timeout) {
06455          ring_timeout = ringing_station->station->ring_timeout;
06456          time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
06457          time_left = (ring_timeout * 1000) - time_elapsed;
06458       }
06459 
06460       /* If the time left based on the per-trunk timeouts is smaller than the
06461        * global station ring timeout, use that. */
06462       if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
06463          time_left = final_trunk_time_left;
06464 
06465       /* If there is no time left, the station needs to stop ringing */
06466       if (time_left <= 0) {
06467          AST_LIST_REMOVE_CURRENT(entry);
06468          sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
06469          res = 1;
06470          continue;
06471       }
06472 
06473       /* There is still some time left for this station to ring, so save that
06474        * timeout if it is the first event scheduled to occur */
06475       if (time_left < *timeout)
06476          *timeout = time_left;
06477    }
06478    AST_LIST_TRAVERSE_SAFE_END;
06479 
06480    return res;
06481 }
06482 
06483 /*! \brief Calculate the ring delay for a station
06484  * \note Assumes sla.lock is locked
06485  */
06486 static int sla_calc_station_delays(unsigned int *timeout)
06487 {
06488    struct sla_station *station;
06489    int res = 0;
06490    struct ao2_iterator i;
06491 
06492    i = ao2_iterator_init(sla_stations, 0);
06493    for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
06494       struct sla_ringing_trunk *ringing_trunk;
06495       int time_left;
06496 
06497       /* Ignore stations already ringing */
06498       if (sla_check_ringing_station(station))
06499          continue;
06500 
06501       /* Ignore stations already on a call */
06502       if (sla_check_inuse_station(station))
06503          continue;
06504 
06505       /* Ignore stations that don't have one of their trunks ringing */
06506       if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
06507          continue;
06508 
06509       if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
06510          continue;
06511 
06512       /* If there is no time left, then the station needs to start ringing.
06513        * Return non-zero so that an event will be queued up an event to 
06514        * make that happen. */
06515       if (time_left <= 0) {
06516          res = 1;
06517          continue;
06518       }
06519 
06520       if (time_left < *timeout)
06521          *timeout = time_left;
06522    }
06523    ao2_iterator_destroy(&i);
06524 
06525    return res;
06526 }
06527 
06528 /*! \brief Calculate the time until the next known event
06529  *  \note Called with sla.lock locked */
06530 static int sla_process_timers(struct timespec *ts)
06531 {
06532    unsigned int timeout = UINT_MAX;
06533    struct timeval wait;
06534    unsigned int change_made = 0;
06535 
06536    /* Check for ring timeouts on ringing trunks */
06537    if (sla_calc_trunk_timeouts(&timeout))
06538       change_made = 1;
06539 
06540    /* Check for ring timeouts on ringing stations */
06541    if (sla_calc_station_timeouts(&timeout))
06542       change_made = 1;
06543 
06544    /* Check for station ring delays */
06545    if (sla_calc_station_delays(&timeout))
06546       change_made = 1;
06547 
06548    /* queue reprocessing of ringing trunks */
06549    if (change_made)
06550       sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
06551 
06552    /* No timeout */
06553    if (timeout == UINT_MAX)
06554       return 0;
06555 
06556    if (ts) {
06557       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
06558       ts->tv_sec = wait.tv_sec;
06559       ts->tv_nsec = wait.tv_usec * 1000;
06560    }
06561 
06562    return 1;
06563 }
06564 
06565 static void sla_event_destroy(struct sla_event *event)
06566 {
06567    if (event->trunk_ref) {
06568       ao2_ref(event->trunk_ref, -1);
06569       event->trunk_ref = NULL;
06570    }
06571 
06572    if (event->station) {
06573       ao2_ref(event->station, -1);
06574       event->station = NULL;
06575    }
06576 
06577    ast_free(event);
06578 }
06579 
06580 static void *sla_thread(void *data)
06581 {
06582    struct sla_failed_station *failed_station;
06583    struct sla_ringing_station *ringing_station;
06584 
06585    ast_mutex_lock(&sla.lock);
06586 
06587    while (!sla.stop) {
06588       struct sla_event *event;
06589       struct timespec ts = { 0, };
06590       unsigned int have_timeout = 0;
06591 
06592       if (AST_LIST_EMPTY(&sla.event_q)) {
06593          if ((have_timeout = sla_process_timers(&ts)))
06594             ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
06595          else
06596             ast_cond_wait(&sla.cond, &sla.lock);
06597          if (sla.stop)
06598             break;
06599       }
06600 
06601       if (have_timeout)
06602          sla_process_timers(NULL);
06603 
06604       while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
06605          ast_mutex_unlock(&sla.lock);
06606          switch (event->type) {
06607          case SLA_EVENT_HOLD:
06608             sla_handle_hold_event(event);
06609             break;
06610          case SLA_EVENT_DIAL_STATE:
06611             sla_handle_dial_state_event();
06612             break;
06613          case SLA_EVENT_RINGING_TRUNK:
06614             sla_handle_ringing_trunk_event();
06615             break;
06616          }
06617          sla_event_destroy(event);
06618          ast_mutex_lock(&sla.lock);
06619       }
06620    }
06621 
06622    ast_mutex_unlock(&sla.lock);
06623 
06624    while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry))) {
06625       sla_ringing_station_destroy(ringing_station);
06626    }
06627 
06628    while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry))) {
06629       sla_failed_station_destroy(failed_station);
06630    }
06631 
06632    return NULL;
06633 }
06634 
06635 struct dial_trunk_args {
06636    struct sla_trunk_ref *trunk_ref;
06637    struct sla_station *station;
06638    ast_mutex_t *cond_lock;
06639    ast_cond_t *cond;
06640 };
06641 
06642 static void *dial_trunk(void *data)
06643 {
06644    struct dial_trunk_args *args = data;
06645    struct ast_dial *dial;
06646    char *tech, *tech_data;
06647    enum ast_dial_result dial_res;
06648    char conf_name[MAX_CONFNUM];
06649    struct ast_conference *conf;
06650    struct ast_flags64 conf_flags = { 0 };
06651    RAII_VAR(struct sla_trunk_ref *, trunk_ref, args->trunk_ref, ao2_cleanup);
06652    RAII_VAR(struct sla_station *, station, args->station, ao2_cleanup);
06653    int caller_is_saved;
06654    struct ast_party_caller caller;
06655    int last_state = 0;
06656    int current_state = 0;
06657 
06658    if (!(dial = ast_dial_create())) {
06659       ast_mutex_lock(args->cond_lock);
06660       ast_cond_signal(args->cond);
06661       ast_mutex_unlock(args->cond_lock);
06662       return NULL;
06663    }
06664 
06665    tech_data = ast_strdupa(trunk_ref->trunk->device);
06666    tech = strsep(&tech_data, "/");
06667    if (ast_dial_append(dial, tech, tech_data) == -1) {
06668       ast_mutex_lock(args->cond_lock);
06669       ast_cond_signal(args->cond);
06670       ast_mutex_unlock(args->cond_lock);
06671       ast_dial_destroy(dial);
06672       return NULL;
06673    }
06674 
06675    /* Do we need to save of the caller ID data? */
06676    caller_is_saved = 0;
06677    if (!sla.attempt_callerid) {
06678       caller_is_saved = 1;
06679       caller = *ast_channel_caller(trunk_ref->chan);
06680       ast_party_caller_init(ast_channel_caller(trunk_ref->chan));
06681    }
06682 
06683    dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
06684 
06685    /* Restore saved caller ID */
06686    if (caller_is_saved) {
06687       ast_party_caller_free(ast_channel_caller(trunk_ref->chan));
06688       ast_channel_caller_set(trunk_ref->chan, &caller);
06689    }
06690 
06691    if (dial_res != AST_DIAL_RESULT_TRYING) {
06692       ast_mutex_lock(args->cond_lock);
06693       ast_cond_signal(args->cond);
06694       ast_mutex_unlock(args->cond_lock);
06695       ast_dial_destroy(dial);
06696       return NULL;
06697    }
06698 
06699    for (;;) {
06700       unsigned int done = 0;
06701       switch ((dial_res = ast_dial_state(dial))) {
06702       case AST_DIAL_RESULT_ANSWERED:
06703          trunk_ref->trunk->chan = ast_dial_answered(dial);
06704       case AST_DIAL_RESULT_HANGUP:
06705       case AST_DIAL_RESULT_INVALID:
06706       case AST_DIAL_RESULT_FAILED:
06707       case AST_DIAL_RESULT_TIMEOUT:
06708       case AST_DIAL_RESULT_UNANSWERED:
06709          done = 1;
06710          break;
06711       case AST_DIAL_RESULT_TRYING:
06712          current_state = AST_CONTROL_PROGRESS;
06713          break;
06714       case AST_DIAL_RESULT_RINGING:
06715       case AST_DIAL_RESULT_PROGRESS:
06716       case AST_DIAL_RESULT_PROCEEDING:
06717          current_state = AST_CONTROL_RINGING;
06718          break;
06719       }
06720       if (done)
06721          break;
06722 
06723       /* check that SLA station that originated trunk call is still alive */
06724       if (station && ast_device_state(station->device) == AST_DEVICE_NOT_INUSE) {
06725          ast_debug(3, "Originating station device %s no longer active\n", station->device);
06726          trunk_ref->trunk->chan = NULL;
06727          break;
06728       }
06729 
06730       /* If trunk line state changed, send indication back to originating SLA Station channel */
06731       if (current_state != last_state) {
06732          ast_debug(3, "Indicating State Change %d to channel %s\n", current_state, ast_channel_name(trunk_ref->chan));
06733          ast_indicate(trunk_ref->chan, current_state);
06734          last_state = current_state;
06735       }
06736 
06737       /* avoid tight loop... sleep for 1/10th second */
06738       ast_safe_sleep(trunk_ref->chan, 100);
06739    }
06740 
06741    if (!trunk_ref->trunk->chan) {
06742       ast_mutex_lock(args->cond_lock);
06743       ast_cond_signal(args->cond);
06744       ast_mutex_unlock(args->cond_lock);
06745       ast_dial_join(dial);
06746       ast_dial_destroy(dial);
06747       return NULL;
06748    }
06749 
06750    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06751    ast_set_flag64(&conf_flags, 
06752       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | 
06753       CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
06754    conf = build_conf(conf_name, "", "", 1, 1, 1, trunk_ref->trunk->chan, NULL);
06755 
06756    ast_mutex_lock(args->cond_lock);
06757    ast_cond_signal(args->cond);
06758    ast_mutex_unlock(args->cond_lock);
06759 
06760    if (conf) {
06761       conf_run(trunk_ref->trunk->chan, conf, &conf_flags, NULL);
06762       dispose_conf(conf);
06763       conf = NULL;
06764    }
06765 
06766    /* If the trunk is going away, it is definitely now IDLE. */
06767    sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06768 
06769    trunk_ref->trunk->chan = NULL;
06770    trunk_ref->trunk->on_hold = 0;
06771 
06772    ast_dial_join(dial);
06773    ast_dial_destroy(dial);
06774 
06775    return NULL;
06776 }
06777 
06778 /*!
06779  * \brief For a given station, choose the highest priority idle trunk
06780  * \pre sla_station is locked
06781  */
06782 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
06783 {
06784    struct sla_trunk_ref *trunk_ref = NULL;
06785 
06786    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06787       if (trunk_ref->state == SLA_TRUNK_STATE_IDLE) {
06788          ao2_ref(trunk_ref, 1);
06789          break;
06790       }
06791    }
06792 
06793    return trunk_ref;
06794 }
06795 
06796 static int sla_station_exec(struct ast_channel *chan, const char *data)
06797 {
06798    char *station_name, *trunk_name;
06799    RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
06800    RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
06801    char conf_name[MAX_CONFNUM];
06802    struct ast_flags64 conf_flags = { 0 };
06803    struct ast_conference *conf;
06804 
06805    if (ast_strlen_zero(data)) {
06806       ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06807       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06808       return 0;
06809    }
06810 
06811    trunk_name = ast_strdupa(data);
06812    station_name = strsep(&trunk_name, "_");
06813 
06814    if (ast_strlen_zero(station_name)) {
06815       ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06816       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06817       return 0;
06818    }
06819 
06820    station = sla_find_station(station_name);
06821 
06822    if (!station) {
06823       ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
06824       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06825       return 0;
06826    }
06827 
06828    ao2_lock(station);
06829    if (!ast_strlen_zero(trunk_name)) {
06830       trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
06831    } else {
06832       trunk_ref = sla_choose_idle_trunk(station);
06833    }
06834    ao2_unlock(station);
06835 
06836    if (!trunk_ref) {
06837       if (ast_strlen_zero(trunk_name))
06838          ast_log(LOG_NOTICE, "No trunks available for call.\n");
06839       else {
06840          ast_log(LOG_NOTICE, "Can't join existing call on trunk "
06841             "'%s' due to access controls.\n", trunk_name);
06842       }
06843       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06844       return 0;
06845    }
06846 
06847    if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
06848       if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
06849          sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06850       else {
06851          trunk_ref->state = SLA_TRUNK_STATE_UP;
06852          ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE,
06853                     "SLA:%s_%s", station->name, trunk_ref->trunk->name);
06854       }
06855    } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
06856       struct sla_ringing_trunk *ringing_trunk;
06857 
06858       ast_mutex_lock(&sla.lock);
06859       AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06860          if (ringing_trunk->trunk == trunk_ref->trunk) {
06861             AST_LIST_REMOVE_CURRENT(entry);
06862             break;
06863          }
06864       }
06865       AST_LIST_TRAVERSE_SAFE_END
06866       ast_mutex_unlock(&sla.lock);
06867 
06868       if (ringing_trunk) {
06869          answer_trunk_chan(ringing_trunk->trunk->chan);
06870          sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06871 
06872          sla_ringing_trunk_destroy(ringing_trunk);
06873 
06874          /* Queue up reprocessing ringing trunks, and then ringing stations again */
06875          sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06876          sla_queue_event(SLA_EVENT_DIAL_STATE);
06877       }
06878    }
06879 
06880    trunk_ref->chan = chan;
06881 
06882    if (!trunk_ref->trunk->chan) {
06883       ast_mutex_t cond_lock;
06884       ast_cond_t cond;
06885       pthread_t dont_care;
06886       struct dial_trunk_args args = {
06887          .trunk_ref = trunk_ref,
06888          .station = station,
06889          .cond_lock = &cond_lock,
06890          .cond = &cond,
06891       };
06892       ao2_ref(trunk_ref, 1);
06893       ao2_ref(station, 1);
06894       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06895       /* Create a thread to dial the trunk and dump it into the conference.
06896        * However, we want to wait until the trunk has been dialed and the
06897        * conference is created before continuing on here. */
06898       ast_autoservice_start(chan);
06899       ast_mutex_init(&cond_lock);
06900       ast_cond_init(&cond, NULL);
06901       ast_mutex_lock(&cond_lock);
06902       ast_pthread_create_detached_background(&dont_care, NULL, dial_trunk, &args);
06903       ast_cond_wait(&cond, &cond_lock);
06904       ast_mutex_unlock(&cond_lock);
06905       ast_mutex_destroy(&cond_lock);
06906       ast_cond_destroy(&cond);
06907       ast_autoservice_stop(chan);
06908       if (!trunk_ref->trunk->chan) {
06909          ast_debug(1, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
06910          pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06911          sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06912          trunk_ref->chan = NULL;
06913          return 0;
06914       }
06915    }
06916 
06917    if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
06918       trunk_ref->trunk->on_hold) {
06919       trunk_ref->trunk->on_hold = 0;
06920       ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
06921       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06922    }
06923 
06924    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06925    ast_set_flag64(&conf_flags,
06926       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
06927    ast_answer(chan);
06928    conf = build_conf(conf_name, "", "", 0, 0, 1, chan, NULL);
06929    if (conf) {
06930       conf_run(chan, conf, &conf_flags, NULL);
06931       dispose_conf(conf);
06932       conf = NULL;
06933    }
06934    trunk_ref->chan = NULL;
06935    if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
06936       trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
06937       strncat(conf_name, ",K", sizeof(conf_name) - strlen(conf_name) - 1);
06938       admin_exec(NULL, conf_name);
06939       trunk_ref->trunk->hold_stations = 0;
06940       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06941    }
06942    
06943    pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
06944 
06945    return 0;
06946 }
06947 
06948 static void sla_trunk_ref_destructor(void *obj)
06949 {
06950    struct sla_trunk_ref *trunk_ref = obj;
06951 
06952    if (trunk_ref->trunk) {
06953       ao2_ref(trunk_ref->trunk, -1);
06954       trunk_ref->trunk = NULL;
06955    }
06956 }
06957 
06958 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
06959 {
06960    struct sla_trunk_ref *trunk_ref;
06961 
06962    if (!(trunk_ref = ao2_alloc(sizeof(*trunk_ref), sla_trunk_ref_destructor))) {
06963       return NULL;
06964    }
06965 
06966    ao2_ref(trunk, 1);
06967    trunk_ref->trunk = trunk;
06968 
06969    return trunk_ref;
06970 }
06971 
06972 static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
06973 {
06974    struct sla_ringing_trunk *ringing_trunk;
06975 
06976    if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk)))) {
06977       return NULL;
06978    }
06979 
06980    ao2_ref(trunk, 1);
06981    ringing_trunk->trunk = trunk;
06982    ringing_trunk->ring_begin = ast_tvnow();
06983 
06984    sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
06985 
06986    ast_mutex_lock(&sla.lock);
06987    AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
06988    ast_mutex_unlock(&sla.lock);
06989 
06990    sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06991 
06992    return ringing_trunk;
06993 }
06994 
06995 static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk)
06996 {
06997    if (ringing_trunk->trunk) {
06998       ao2_ref(ringing_trunk->trunk, -1);
06999       ringing_trunk->trunk = NULL;
07000    }
07001 
07002    ast_free(ringing_trunk);
07003 }
07004 
07005 enum {
07006    SLA_TRUNK_OPT_MOH = (1 << 0),
07007 };
07008 
07009 enum {
07010    SLA_TRUNK_OPT_ARG_MOH_CLASS = 0,
07011    SLA_TRUNK_OPT_ARG_ARRAY_SIZE = 1,
07012 };
07013 
07014 AST_APP_OPTIONS(sla_trunk_opts, BEGIN_OPTIONS
07015    AST_APP_OPTION_ARG('M', SLA_TRUNK_OPT_MOH, SLA_TRUNK_OPT_ARG_MOH_CLASS),
07016 END_OPTIONS );
07017 
07018 static int sla_trunk_exec(struct ast_channel *chan, const char *data)
07019 {
07020    char conf_name[MAX_CONFNUM];
07021    struct ast_conference *conf;
07022    struct ast_flags64 conf_flags = { 0 };
07023    RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
07024    struct sla_ringing_trunk *ringing_trunk;
07025    AST_DECLARE_APP_ARGS(args,
07026       AST_APP_ARG(trunk_name);
07027       AST_APP_ARG(options);
07028    );
07029    char *opts[SLA_TRUNK_OPT_ARG_ARRAY_SIZE] = { NULL, };
07030    struct ast_flags opt_flags = { 0 };
07031    char *parse;
07032 
07033    if (ast_strlen_zero(data)) {
07034       ast_log(LOG_ERROR, "The SLATrunk application requires an argument, the trunk name\n");
07035       return -1;
07036    }
07037 
07038    parse = ast_strdupa(data);
07039    AST_STANDARD_APP_ARGS(args, parse);
07040    if (args.argc == 2) {
07041       if (ast_app_parse_options(sla_trunk_opts, &opt_flags, opts, args.options)) {
07042          ast_log(LOG_ERROR, "Error parsing options for SLATrunk\n");
07043          return -1;
07044       }
07045    }
07046 
07047    trunk = sla_find_trunk(args.trunk_name);
07048 
07049    if (!trunk) {
07050       ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", args.trunk_name);
07051       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
07052       return 0;
07053    }
07054 
07055    if (trunk->chan) {
07056       ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
07057          args.trunk_name);
07058       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
07059       return 0;
07060    }
07061 
07062    trunk->chan = chan;
07063 
07064    if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
07065       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
07066       return 0;
07067    }
07068 
07069    snprintf(conf_name, sizeof(conf_name), "SLA_%s", args.trunk_name);
07070    conf = build_conf(conf_name, "", "", 1, 1, 1, chan, NULL);
07071    if (!conf) {
07072       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
07073       return 0;
07074    }
07075    ast_set_flag64(&conf_flags, 
07076       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF | CONFFLAG_NO_AUDIO_UNTIL_UP);
07077 
07078    if (ast_test_flag(&opt_flags, SLA_TRUNK_OPT_MOH)) {
07079       ast_indicate(chan, -1);
07080       ast_set_flag64(&conf_flags, CONFFLAG_MOH);
07081    } else
07082       ast_indicate(chan, AST_CONTROL_RINGING);
07083 
07084    conf_run(chan, conf, &conf_flags, opts);
07085    dispose_conf(conf);
07086    conf = NULL;
07087    trunk->chan = NULL;
07088    trunk->on_hold = 0;
07089 
07090    sla_change_trunk_state(trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
07091 
07092    if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
07093       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
07094 
07095    /* Remove the entry from the list of ringing trunks if it is still there. */
07096    ast_mutex_lock(&sla.lock);
07097    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
07098       if (ringing_trunk->trunk == trunk) {
07099          AST_LIST_REMOVE_CURRENT(entry);
07100          break;
07101       }
07102    }
07103    AST_LIST_TRAVERSE_SAFE_END;
07104    ast_mutex_unlock(&sla.lock);
07105    if (ringing_trunk) {
07106       sla_ringing_trunk_destroy(ringing_trunk);
07107       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
07108       /* Queue reprocessing of ringing trunks to make stations stop ringing
07109        * that shouldn't be ringing after this trunk stopped. */
07110       sla_queue_event(SLA_EVENT_RINGING_TRUNK);
07111    }
07112 
07113    return 0;
07114 }
07115 
07116 static enum ast_device_state sla_state(const char *data)
07117 {
07118    char *buf, *station_name, *trunk_name;
07119    RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
07120    struct sla_trunk_ref *trunk_ref;
07121    enum ast_device_state res = AST_DEVICE_INVALID;
07122 
07123    trunk_name = buf = ast_strdupa(data);
07124    station_name = strsep(&trunk_name, "_");
07125 
07126    station = sla_find_station(station_name);
07127    if (station) {
07128       ao2_lock(station);
07129       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07130          if (!strcasecmp(trunk_name, trunk_ref->trunk->name)) {
07131             res = sla_state_to_devstate(trunk_ref->state);
07132             break;
07133          }
07134       }
07135       ao2_unlock(station);
07136    }
07137 
07138    if (res == AST_DEVICE_INVALID) {
07139       ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
07140          trunk_name, station_name);
07141    }
07142 
07143    return res;
07144 }
07145 
07146 static int sla_trunk_release_refs(void *obj, void *arg, int flags)
07147 {
07148    struct sla_trunk *trunk = obj;
07149    struct sla_station_ref *station_ref;
07150 
07151    while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry))) {
07152       ao2_ref(station_ref, -1);
07153    }
07154 
07155    return 0;
07156 }
07157 
07158 static int sla_station_release_refs(void *obj, void *arg, int flags)
07159 {
07160    struct sla_station *station = obj;
07161    struct sla_trunk_ref *trunk_ref;
07162 
07163    while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry))) {
07164       ao2_ref(trunk_ref, -1);
07165    }
07166 
07167    return 0;
07168 }
07169 
07170 static void sla_station_destructor(void *obj)
07171 {
07172    struct sla_station *station = obj;
07173 
07174    ast_debug(1, "sla_station destructor for '%s'\n", station->name);
07175 
07176    if (!ast_strlen_zero(station->autocontext)) {
07177       struct sla_trunk_ref *trunk_ref;
07178 
07179       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07180          char exten[AST_MAX_EXTENSION];
07181          char hint[AST_MAX_APP];
07182          snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
07183          snprintf(hint, sizeof(hint), "SLA:%s", exten);
07184          ast_context_remove_extension(station->autocontext, exten, 
07185             1, sla_registrar);
07186          ast_context_remove_extension(station->autocontext, hint, 
07187             PRIORITY_HINT, sla_registrar);
07188       }
07189    }
07190 
07191    sla_station_release_refs(station, NULL, 0);
07192 
07193    ast_string_field_free_memory(station);
07194 }
07195 
07196 static int sla_trunk_hash(const void *obj, const int flags)
07197 {
07198    const struct sla_trunk *trunk = obj;
07199 
07200    return ast_str_case_hash(trunk->name);
07201 }
07202 
07203 static int sla_trunk_cmp(void *obj, void *arg, int flags)
07204 {
07205    struct sla_trunk *trunk = obj, *trunk2 = arg;
07206 
07207    return !strcasecmp(trunk->name, trunk2->name) ? CMP_MATCH | CMP_STOP : 0;
07208 }
07209 
07210 static int sla_station_hash(const void *obj, const int flags)
07211 {
07212    const struct sla_station *station = obj;
07213 
07214    return ast_str_case_hash(station->name);
07215 }
07216 
07217 static int sla_station_cmp(void *obj, void *arg, int flags)
07218 {
07219    struct sla_station *station = obj, *station2 = arg;
07220 
07221    return !strcasecmp(station->name, station2->name) ? CMP_MATCH | CMP_STOP : 0;
07222 }
07223 
07224 static void sla_destroy(void)
07225 {
07226    if (sla.thread != AST_PTHREADT_NULL) {
07227       ast_mutex_lock(&sla.lock);
07228       sla.stop = 1;
07229       ast_cond_signal(&sla.cond);
07230       ast_mutex_unlock(&sla.lock);
07231       pthread_join(sla.thread, NULL);
07232    }
07233 
07234    /* Drop any created contexts from the dialplan */
07235    ast_context_destroy(NULL, sla_registrar);
07236 
07237    ast_mutex_destroy(&sla.lock);
07238    ast_cond_destroy(&sla.cond);
07239 
07240    ao2_callback(sla_trunks, 0, sla_trunk_release_refs, NULL);
07241    ao2_callback(sla_stations, 0, sla_station_release_refs, NULL);
07242 
07243    ao2_ref(sla_trunks, -1);
07244    sla_trunks = NULL;
07245 
07246    ao2_ref(sla_stations, -1);
07247    sla_stations = NULL;
07248 }
07249 
07250 static int sla_check_device(const char *device)
07251 {
07252    char *tech, *tech_data;
07253 
07254    tech_data = ast_strdupa(device);
07255    tech = strsep(&tech_data, "/");
07256 
07257    if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
07258       return -1;
07259 
07260    return 0;
07261 }
07262 
07263 static void sla_trunk_destructor(void *obj)
07264 {
07265    struct sla_trunk *trunk = obj;
07266 
07267    ast_debug(1, "sla_trunk destructor for '%s'\n", trunk->name);
07268 
07269    if (!ast_strlen_zero(trunk->autocontext)) {
07270       ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
07271    }
07272 
07273    sla_trunk_release_refs(trunk, NULL, 0);
07274 
07275    ast_string_field_free_memory(trunk);
07276 }
07277 
07278 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
07279 {
07280    RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
07281    struct ast_variable *var;
07282    const char *dev;
07283    int existing_trunk = 0;
07284 
07285    if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
07286       ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
07287       return -1;
07288    }
07289 
07290    if (sla_check_device(dev)) {
07291       ast_log(LOG_ERROR, "SLA Trunk '%s' defined with invalid device '%s'!\n",
07292          cat, dev);
07293       return -1;
07294    }
07295 
07296    if ((trunk = sla_find_trunk(cat))) {
07297       trunk->mark = 0;
07298       existing_trunk = 1;
07299    } else if ((trunk = ao2_alloc(sizeof(*trunk), sla_trunk_destructor))) {
07300       if (ast_string_field_init(trunk, 32)) {
07301          return -1;
07302       }
07303       ast_string_field_set(trunk, name, cat);
07304    } else {
07305       return -1;
07306    }
07307 
07308    ao2_lock(trunk);
07309 
07310    ast_string_field_set(trunk, device, dev);
07311 
07312    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
07313       if (!strcasecmp(var->name, "autocontext"))
07314          ast_string_field_set(trunk, autocontext, var->value);
07315       else if (!strcasecmp(var->name, "ringtimeout")) {
07316          if (sscanf(var->value, "%30u", &trunk->ring_timeout) != 1) {
07317             ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
07318                var->value, trunk->name);
07319             trunk->ring_timeout = 0;
07320          }
07321       } else if (!strcasecmp(var->name, "barge"))
07322          trunk->barge_disabled = ast_false(var->value);
07323       else if (!strcasecmp(var->name, "hold")) {
07324          if (!strcasecmp(var->value, "private"))
07325             trunk->hold_access = SLA_HOLD_PRIVATE;
07326          else if (!strcasecmp(var->value, "open"))
07327             trunk->hold_access = SLA_HOLD_OPEN;
07328          else {
07329             ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
07330                var->value, trunk->name);
07331          }
07332       } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
07333          ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
07334             var->name, var->lineno, SLA_CONFIG_FILE);
07335       }
07336    }
07337 
07338    ao2_unlock(trunk);
07339 
07340    if (!ast_strlen_zero(trunk->autocontext)) {
07341       struct ast_context *context;
07342       context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
07343       if (!context) {
07344          ast_log(LOG_ERROR, "Failed to automatically find or create "
07345             "context '%s' for SLA!\n", trunk->autocontext);
07346          return -1;
07347       }
07348       if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
07349          NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
07350          ast_log(LOG_ERROR, "Failed to automatically create extension "
07351             "for trunk '%s'!\n", trunk->name);
07352          return -1;
07353       }
07354    }
07355 
07356    if (!existing_trunk) {
07357       ao2_link(sla_trunks, trunk);
07358    }
07359 
07360    return 0;
07361 }
07362 
07363 /*!
07364  * \internal
07365  * \pre station is not locked
07366  */
07367 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
07368 {
07369    RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
07370    struct sla_trunk_ref *trunk_ref = NULL;
07371    struct sla_station_ref *station_ref;
07372    char *trunk_name, *options, *cur;
07373    int existing_trunk_ref = 0;
07374    int existing_station_ref = 0;
07375 
07376    options = ast_strdupa(var->value);
07377    trunk_name = strsep(&options, ",");
07378 
07379    trunk = sla_find_trunk(trunk_name);
07380    if (!trunk) {
07381       ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
07382       return;
07383    }
07384 
07385    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07386       if (trunk_ref->trunk == trunk) {
07387          trunk_ref->mark = 0;
07388          existing_trunk_ref = 1;
07389          break;
07390       }
07391    }
07392 
07393    if (!trunk_ref && !(trunk_ref = create_trunk_ref(trunk))) {
07394       return;
07395    }
07396 
07397    trunk_ref->state = SLA_TRUNK_STATE_IDLE;
07398 
07399    while ((cur = strsep(&options, ","))) {
07400       char *name, *value = cur;
07401       name = strsep(&value, "=");
07402       if (!strcasecmp(name, "ringtimeout")) {
07403          if (sscanf(value, "%30u", &trunk_ref->ring_timeout) != 1) {
07404             ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
07405                "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
07406             trunk_ref->ring_timeout = 0;
07407          }
07408       } else if (!strcasecmp(name, "ringdelay")) {
07409          if (sscanf(value, "%30u", &trunk_ref->ring_delay) != 1) {
07410             ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
07411                "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
07412             trunk_ref->ring_delay = 0;
07413          }
07414       } else {
07415          ast_log(LOG_WARNING, "Invalid option '%s' for "
07416             "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
07417       }
07418    }
07419 
07420    AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
07421       if (station_ref->station == station) {
07422          station_ref->mark = 0;
07423          existing_station_ref = 1;
07424          break;
07425       }
07426    }
07427 
07428    if (!station_ref && !(station_ref = sla_create_station_ref(station))) {
07429       if (!existing_trunk_ref) {
07430          ao2_ref(trunk_ref, -1);
07431       } else {
07432          trunk_ref->mark = 1;
07433       }
07434       return;
07435    }
07436 
07437    if (!existing_station_ref) {
07438       ao2_lock(trunk);
07439       AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
07440       ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
07441       ao2_unlock(trunk);
07442    }
07443 
07444    if (!existing_trunk_ref) {
07445       ao2_lock(station);
07446       AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
07447       ao2_unlock(station);
07448    }
07449 }
07450 
07451 static int sla_build_station(struct ast_config *cfg, const char *cat)
07452 {
07453    RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
07454    struct ast_variable *var;
07455    const char *dev;
07456    int existing_station = 0;
07457 
07458    if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
07459       ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
07460       return -1;
07461    }
07462 
07463    if ((station = sla_find_station(cat))) {
07464       station->mark = 0;
07465       existing_station = 1;
07466    } else if ((station = ao2_alloc(sizeof(*station), sla_station_destructor))) {
07467       if (ast_string_field_init(station, 32)) {
07468          return -1;
07469       }
07470       ast_string_field_set(station, name, cat);
07471    } else {
07472       return -1;
07473    }
07474 
07475    ao2_lock(station);
07476 
07477    ast_string_field_set(station, device, dev);
07478 
07479    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
07480       if (!strcasecmp(var->name, "trunk")) {
07481          ao2_unlock(station);
07482          sla_add_trunk_to_station(station, var);
07483          ao2_lock(station);
07484       } else if (!strcasecmp(var->name, "autocontext")) {
07485          ast_string_field_set(station, autocontext, var->value);
07486       } else if (!strcasecmp(var->name, "ringtimeout")) {
07487          if (sscanf(var->value, "%30u", &station->ring_timeout) != 1) {
07488             ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
07489                var->value, station->name);
07490             station->ring_timeout = 0;
07491          }
07492       } else if (!strcasecmp(var->name, "ringdelay")) {
07493          if (sscanf(var->value, "%30u", &station->ring_delay) != 1) {
07494             ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
07495                var->value, station->name);
07496             station->ring_delay = 0;
07497          }
07498       } else if (!strcasecmp(var->name, "hold")) {
07499          if (!strcasecmp(var->value, "private"))
07500             station->hold_access = SLA_HOLD_PRIVATE;
07501          else if (!strcasecmp(var->value, "open"))
07502             station->hold_access = SLA_HOLD_OPEN;
07503          else {
07504             ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
07505                var->value, station->name);
07506          }
07507 
07508       } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
07509          ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
07510             var->name, var->lineno, SLA_CONFIG_FILE);
07511       }
07512    }
07513 
07514    ao2_unlock(station);
07515 
07516    if (!ast_strlen_zero(station->autocontext)) {
07517       struct ast_context *context;
07518       struct sla_trunk_ref *trunk_ref;
07519       context = ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar);
07520       if (!context) {
07521          ast_log(LOG_ERROR, "Failed to automatically find or create "
07522             "context '%s' for SLA!\n", station->autocontext);
07523          return -1;
07524       }
07525       /* The extension for when the handset goes off-hook.
07526        * exten => station1,1,SLAStation(station1) */
07527       if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
07528          NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
07529          ast_log(LOG_ERROR, "Failed to automatically create extension "
07530             "for trunk '%s'!\n", station->name);
07531          return -1;
07532       }
07533       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07534          char exten[AST_MAX_EXTENSION];
07535          char hint[AST_MAX_APP];
07536          snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
07537          snprintf(hint, sizeof(hint), "SLA:%s", exten);
07538          /* Extension for this line button 
07539           * exten => station1_line1,1,SLAStation(station1_line1) */
07540          if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
07541             NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
07542             ast_log(LOG_ERROR, "Failed to automatically create extension "
07543                "for trunk '%s'!\n", station->name);
07544             return -1;
07545          }
07546          /* Hint for this line button 
07547           * exten => station1_line1,hint,SLA:station1_line1 */
07548          if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
07549             NULL, NULL, hint, NULL, NULL, sla_registrar)) {
07550             ast_log(LOG_ERROR, "Failed to automatically create hint "
07551                "for trunk '%s'!\n", station->name);
07552             return -1;
07553          }
07554       }
07555    }
07556 
07557    if (!existing_station) {
07558       ao2_link(sla_stations, station);
07559    }
07560 
07561    return 0;
07562 }
07563 
07564 static int sla_trunk_mark(void *obj, void *arg, int flags)
07565 {
07566    struct sla_trunk *trunk = obj;
07567    struct sla_station_ref *station_ref;
07568 
07569    ao2_lock(trunk);
07570 
07571    trunk->mark = 1;
07572 
07573    AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
07574       station_ref->mark = 1;
07575    }
07576 
07577    ao2_unlock(trunk);
07578 
07579    return 0;
07580 }
07581 
07582 static int sla_station_mark(void *obj, void *arg, int flags)
07583 {
07584    struct sla_station *station = obj;
07585    struct sla_trunk_ref *trunk_ref;
07586 
07587    ao2_lock(station);
07588 
07589    station->mark = 1;
07590 
07591    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07592       trunk_ref->mark = 1;
07593    }
07594 
07595    ao2_unlock(station);
07596 
07597    return 0;
07598 }
07599 
07600 static int sla_trunk_is_marked(void *obj, void *arg, int flags)
07601 {
07602    struct sla_trunk *trunk = obj;
07603 
07604    ao2_lock(trunk);
07605 
07606    if (trunk->mark) {
07607       /* Only remove all of the station references if the trunk itself is going away */
07608       sla_trunk_release_refs(trunk, NULL, 0);
07609    } else {
07610       struct sla_station_ref *station_ref;
07611 
07612       /* Otherwise only remove references to stations no longer in the config */
07613       AST_LIST_TRAVERSE_SAFE_BEGIN(&trunk->stations, station_ref, entry) {
07614          if (!station_ref->mark) {
07615             continue;
07616          }
07617          AST_LIST_REMOVE_CURRENT(entry);
07618          ao2_ref(station_ref, -1);
07619       }
07620       AST_LIST_TRAVERSE_SAFE_END
07621    }
07622 
07623    ao2_unlock(trunk);
07624 
07625    return trunk->mark ? CMP_MATCH : 0;
07626 }
07627 
07628 static int sla_station_is_marked(void *obj, void *arg, int flags)
07629 {
07630    struct sla_station *station = obj;
07631 
07632    ao2_lock(station);
07633 
07634    if (station->mark) {
07635       /* Only remove all of the trunk references if the station itself is going away */
07636       sla_station_release_refs(station, NULL, 0);
07637    } else {
07638       struct sla_trunk_ref *trunk_ref;
07639 
07640       /* Otherwise only remove references to trunks no longer in the config */
07641       AST_LIST_TRAVERSE_SAFE_BEGIN(&station->trunks, trunk_ref, entry) {
07642          if (!trunk_ref->mark) {
07643             continue;
07644          }
07645          AST_LIST_REMOVE_CURRENT(entry);
07646          ao2_ref(trunk_ref, -1);
07647       }
07648       AST_LIST_TRAVERSE_SAFE_END
07649    }
07650 
07651    ao2_unlock(station);
07652 
07653    return station->mark ? CMP_MATCH : 0;
07654 }
07655 
07656 static int sla_in_use(void)
07657 {
07658    return ao2_container_count(sla_trunks) || ao2_container_count(sla_stations);
07659 }
07660 
07661 static int sla_load_config(int reload)
07662 {
07663    struct ast_config *cfg;
07664    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
07665    const char *cat = NULL;
07666    int res = 0;
07667    const char *val;
07668 
07669    if (!reload) {
07670       ast_mutex_init(&sla.lock);
07671       ast_cond_init(&sla.cond, NULL);
07672       sla_trunks = ao2_container_alloc(1, sla_trunk_hash, sla_trunk_cmp);
07673       sla_stations = ao2_container_alloc(1, sla_station_hash, sla_station_cmp);
07674    }
07675 
07676    if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags))) {
07677       return 0; /* Treat no config as normal */
07678    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
07679       return 0;
07680    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
07681       ast_log(LOG_ERROR, "Config file " SLA_CONFIG_FILE " is in an invalid format.  Aborting.\n");
07682       return 0;
07683    }
07684 
07685    if (reload) {
07686       ao2_callback(sla_trunks, 0, sla_trunk_mark, NULL);
07687       ao2_callback(sla_stations, 0, sla_station_mark, NULL);
07688    }
07689 
07690    if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
07691       sla.attempt_callerid = ast_true(val);
07692 
07693    while ((cat = ast_category_browse(cfg, cat)) && !res) {
07694       const char *type;
07695       if (!strcasecmp(cat, "general"))
07696          continue;
07697       if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
07698          ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
07699             SLA_CONFIG_FILE);
07700          continue;
07701       }
07702       if (!strcasecmp(type, "trunk"))
07703          res = sla_build_trunk(cfg, cat);
07704       else if (!strcasecmp(type, "station"))
07705          res = sla_build_station(cfg, cat);
07706       else {
07707          ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
07708             SLA_CONFIG_FILE, type);
07709       }
07710    }
07711 
07712    ast_config_destroy(cfg);
07713 
07714    if (reload) {
07715       ao2_callback(sla_trunks, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_trunk_is_marked, NULL);
07716       ao2_callback(sla_stations, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_station_is_marked, NULL);
07717    }
07718 
07719    /* Start SLA event processing thread once SLA has been configured. */
07720    if (sla.thread == AST_PTHREADT_NULL && sla_in_use()) {
07721       ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
07722    }
07723 
07724    return res;
07725 }
07726 
07727 static int acf_meetme_info_eval(const char *keyword, const struct ast_conference *conf)
07728 {
07729    if (!strcasecmp("lock", keyword)) {
07730       return conf->locked;
07731    } else if (!strcasecmp("parties", keyword)) {
07732       return conf->users;
07733    } else if (!strcasecmp("activity", keyword)) {
07734       time_t now;
07735       now = time(NULL);
07736       return (now - conf->start);
07737    } else if (!strcasecmp("dynamic", keyword)) {
07738       return conf->isdynamic;
07739    } else {
07740       return -1;
07741    }
07742 
07743 }
07744 
07745 static int acf_meetme_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
07746 {
07747    struct ast_conference *conf;
07748    char *parse;
07749    int result = -2; /* only non-negative numbers valid, -1 is used elsewhere */
07750    AST_DECLARE_APP_ARGS(args,
07751       AST_APP_ARG(keyword);
07752       AST_APP_ARG(confno);
07753    );
07754 
07755    if (ast_strlen_zero(data)) {
07756       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires two arguments\n");
07757       return -1;
07758    }
07759 
07760    parse = ast_strdupa(data);
07761    AST_STANDARD_APP_ARGS(args, parse);
07762 
07763    if (ast_strlen_zero(args.keyword)) {
07764       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a keyword\n");
07765       return -1;
07766    }
07767 
07768    if (ast_strlen_zero(args.confno)) {
07769       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a conference number\n");
07770       return -1;
07771    }
07772 
07773    AST_LIST_LOCK(&confs);
07774    AST_LIST_TRAVERSE(&confs, conf, list) {
07775       if (!strcmp(args.confno, conf->confno)) {
07776          result = acf_meetme_info_eval(args.keyword, conf);
07777          break;
07778       }
07779    }
07780    AST_LIST_UNLOCK(&confs);
07781 
07782    if (result > -1) {
07783       snprintf(buf, len, "%d", result);
07784    } else if (result == -1) {
07785       ast_log(LOG_NOTICE, "Error: invalid keyword: '%s'\n", args.keyword);
07786       snprintf(buf, len, "0");
07787    } else if (result == -2) {
07788       ast_log(LOG_NOTICE, "Error: conference (%s) not found\n", args.confno); 
07789       snprintf(buf, len, "0");
07790    }
07791 
07792    return 0;
07793 }
07794 
07795 
07796 static struct ast_custom_function meetme_info_acf = {
07797    .name = "MEETME_INFO",
07798    .read = acf_meetme_info,
07799 };
07800 
07801 
07802 static int load_config(int reload)
07803 {
07804    load_config_meetme();
07805    return sla_load_config(reload);
07806 }
07807 
07808 #define MEETME_DATA_EXPORT(MEMBER)              \
07809    MEMBER(ast_conference, confno, AST_DATA_STRING)       \
07810    MEMBER(ast_conference, dahdiconf, AST_DATA_INTEGER)      \
07811    MEMBER(ast_conference, users, AST_DATA_INTEGER)       \
07812    MEMBER(ast_conference, markedusers, AST_DATA_INTEGER)    \
07813    MEMBER(ast_conference, maxusers, AST_DATA_INTEGER)    \
07814    MEMBER(ast_conference, isdynamic, AST_DATA_BOOLEAN)      \
07815    MEMBER(ast_conference, locked, AST_DATA_BOOLEAN)      \
07816    MEMBER(ast_conference, recordingfilename, AST_DATA_STRING)  \
07817    MEMBER(ast_conference, recordingformat, AST_DATA_STRING) \
07818    MEMBER(ast_conference, pin, AST_DATA_PASSWORD)        \
07819    MEMBER(ast_conference, pinadmin, AST_DATA_PASSWORD)      \
07820    MEMBER(ast_conference, start, AST_DATA_TIMESTAMP)     \
07821    MEMBER(ast_conference, endtime, AST_DATA_TIMESTAMP)
07822 
07823 AST_DATA_STRUCTURE(ast_conference, MEETME_DATA_EXPORT);
07824 
07825 #define MEETME_USER_DATA_EXPORT(MEMBER)               \
07826    MEMBER(ast_conf_user, user_no, AST_DATA_INTEGER)      \
07827    MEMBER(ast_conf_user, talking, AST_DATA_BOOLEAN)      \
07828    MEMBER(ast_conf_user, dahdichannel, AST_DATA_BOOLEAN)    \
07829    MEMBER(ast_conf_user, jointime, AST_DATA_TIMESTAMP)      \
07830    MEMBER(ast_conf_user, kicktime, AST_DATA_TIMESTAMP)      \
07831    MEMBER(ast_conf_user, timelimit, AST_DATA_MILLISECONDS)     \
07832    MEMBER(ast_conf_user, play_warning, AST_DATA_MILLISECONDS)  \
07833    MEMBER(ast_conf_user, warning_freq, AST_DATA_MILLISECONDS)
07834 
07835 AST_DATA_STRUCTURE(ast_conf_user, MEETME_USER_DATA_EXPORT);
07836 
07837 static int user_add_provider_cb(void *obj, void *arg, int flags)
07838 {
07839    struct ast_data *data_meetme_user;
07840    struct ast_data *data_meetme_user_channel;
07841    struct ast_data *data_meetme_user_volume;
07842 
07843    struct ast_conf_user *user = obj;
07844    struct ast_data *data_meetme_users = arg;
07845 
07846    data_meetme_user = ast_data_add_node(data_meetme_users, "user");
07847    if (!data_meetme_user) {
07848       return 0;
07849    }
07850    /* user structure */
07851    ast_data_add_structure(ast_conf_user, data_meetme_user, user);
07852 
07853    /* user's channel */
07854    data_meetme_user_channel = ast_data_add_node(data_meetme_user, "channel");
07855    if (!data_meetme_user_channel) {
07856       return 0;
07857    }
07858 
07859    ast_channel_data_add_structure(data_meetme_user_channel, user->chan, 1);
07860 
07861    /* volume structure */
07862    data_meetme_user_volume = ast_data_add_node(data_meetme_user, "listen-volume");
07863    if (!data_meetme_user_volume) {
07864       return 0;
07865    }
07866    ast_data_add_int(data_meetme_user_volume, "desired", user->listen.desired);
07867    ast_data_add_int(data_meetme_user_volume, "actual", user->listen.actual);
07868 
07869    data_meetme_user_volume = ast_data_add_node(data_meetme_user, "talk-volume");
07870    if (!data_meetme_user_volume) {
07871       return 0;
07872    }
07873    ast_data_add_int(data_meetme_user_volume, "desired", user->talk.desired);
07874    ast_data_add_int(data_meetme_user_volume, "actual", user->talk.actual);
07875 
07876    return 0;
07877 }
07878 
07879 /*!
07880  * \internal
07881  * \brief Implements the meetme data provider.
07882  */
07883 static int meetme_data_provider_get(const struct ast_data_search *search,
07884    struct ast_data *data_root)
07885 {
07886    struct ast_conference *cnf;
07887    struct ast_data *data_meetme, *data_meetme_users;
07888 
07889    AST_LIST_LOCK(&confs);
07890    AST_LIST_TRAVERSE(&confs, cnf, list) {
07891       data_meetme = ast_data_add_node(data_root, "meetme");
07892       if (!data_meetme) {
07893          continue;
07894       }
07895 
07896       ast_data_add_structure(ast_conference, data_meetme, cnf);
07897 
07898       if (ao2_container_count(cnf->usercontainer)) {
07899          data_meetme_users = ast_data_add_node(data_meetme, "users");
07900          if (!data_meetme_users) {
07901             ast_data_remove_node(data_root, data_meetme);
07902             continue;
07903          }
07904 
07905          ao2_callback(cnf->usercontainer, OBJ_NODATA, user_add_provider_cb, data_meetme_users); 
07906       }
07907 
07908       if (!ast_data_search_match(search, data_meetme)) {
07909          ast_data_remove_node(data_root, data_meetme);
07910       }
07911    }
07912    AST_LIST_UNLOCK(&confs);
07913 
07914    return 0;
07915 }
07916 
07917 static const struct ast_data_handler meetme_data_provider = {
07918    .version = AST_DATA_HANDLER_VERSION,
07919    .get = meetme_data_provider_get
07920 };
07921 
07922 static const struct ast_data_entry meetme_data_providers[] = {
07923    AST_DATA_ENTRY("asterisk/application/meetme/list", &meetme_data_provider),
07924 };
07925 
07926 #ifdef TEST_FRAMEWORK
07927 AST_TEST_DEFINE(test_meetme_data_provider)
07928 {
07929    struct ast_channel *chan;
07930    struct ast_conference *cnf;
07931    struct ast_data *node;
07932    struct ast_data_query query = {
07933       .path = "/asterisk/application/meetme/list",
07934       .search = "list/meetme/confno=9898"
07935    };
07936 
07937    switch (cmd) {
07938    case TEST_INIT:
07939       info->name = "meetme_get_data_test";
07940       info->category = "/main/data/app_meetme/list/";
07941       info->summary = "Meetme data provider unit test";
07942       info->description =
07943          "Tests whether the Meetme data provider implementation works as expected.";
07944       return AST_TEST_NOT_RUN;
07945    case TEST_EXECUTE:
07946       break;
07947    }
07948 
07949    chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, 0, 0, "MeetMeTest");
07950    if (!chan) {
07951       ast_test_status_update(test, "Channel allocation failed\n");
07952       return AST_TEST_FAIL;
07953    }
07954 
07955    cnf = build_conf("9898", "", "1234", 1, 1, 1, chan, test);
07956    if (!cnf) {
07957       ast_test_status_update(test, "Build of test conference 9898 failed\n");
07958       ast_hangup(chan);
07959       return AST_TEST_FAIL;
07960    }
07961 
07962    node = ast_data_get(&query);
07963    if (!node) {
07964       ast_test_status_update(test, "Data query for test conference 9898 failed\n");
07965       dispose_conf(cnf);
07966       ast_hangup(chan);
07967       return AST_TEST_FAIL;
07968    }
07969 
07970    if (strcmp(ast_data_retrieve_string(node, "meetme/confno"), "9898")) {
07971       ast_test_status_update(test, "Query returned the wrong conference\n");
07972       dispose_conf(cnf);
07973       ast_hangup(chan);
07974       ast_data_free(node);
07975       return AST_TEST_FAIL;
07976    }
07977 
07978    ast_data_free(node);
07979    dispose_conf(cnf);
07980    ast_hangup(chan);
07981 
07982    return AST_TEST_PASS;
07983 }
07984 #endif
07985 
07986 static int unload_module(void)
07987 {
07988    int res = 0;
07989    
07990    ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
07991    res = ast_manager_unregister("MeetmeMute");
07992    res |= ast_manager_unregister("MeetmeUnmute");
07993    res |= ast_manager_unregister("MeetmeList");
07994    res |= ast_manager_unregister("MeetmeListRooms");
07995    res |= ast_unregister_application(app4);
07996    res |= ast_unregister_application(app3);
07997    res |= ast_unregister_application(app2);
07998    res |= ast_unregister_application(app);
07999    res |= ast_unregister_application(slastation_app);
08000    res |= ast_unregister_application(slatrunk_app);
08001 
08002 #ifdef TEST_FRAMEWORK
08003    AST_TEST_UNREGISTER(test_meetme_data_provider);
08004 #endif
08005    ast_data_unregister(NULL);
08006 
08007    ast_devstate_prov_del("Meetme");
08008    ast_devstate_prov_del("SLA");
08009    
08010    sla_destroy();
08011    
08012    res |= ast_custom_function_unregister(&meetme_info_acf);
08013    ast_unload_realtime("meetme");
08014 
08015    return res;
08016 }
08017 
08018 static int load_module(void)
08019 {
08020    int res = 0;
08021 
08022    res |= load_config(0);
08023 
08024    ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
08025    res |= ast_manager_register_xml("MeetmeMute", EVENT_FLAG_CALL, action_meetmemute);
08026    res |= ast_manager_register_xml("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute);
08027    res |= ast_manager_register_xml("MeetmeList", EVENT_FLAG_REPORTING, action_meetmelist);
08028    res |= ast_manager_register_xml("MeetmeListRooms", EVENT_FLAG_REPORTING, action_meetmelistrooms);
08029    res |= ast_register_application_xml(app4, channel_admin_exec);
08030    res |= ast_register_application_xml(app3, admin_exec);
08031    res |= ast_register_application_xml(app2, count_exec);
08032    res |= ast_register_application_xml(app, conf_exec);
08033    res |= ast_register_application_xml(slastation_app, sla_station_exec);
08034    res |= ast_register_application_xml(slatrunk_app, sla_trunk_exec);
08035 
08036 #ifdef TEST_FRAMEWORK
08037    AST_TEST_REGISTER(test_meetme_data_provider);
08038 #endif
08039    ast_data_register_multiple(meetme_data_providers, ARRAY_LEN(meetme_data_providers));
08040 
08041    res |= ast_devstate_prov_add("Meetme", meetmestate);
08042    res |= ast_devstate_prov_add("SLA", sla_state);
08043 
08044    res |= ast_custom_function_register(&meetme_info_acf);
08045    ast_realtime_require_field("meetme", "confno", RQ_UINTEGER2, 3, "members", RQ_UINTEGER1, 3, NULL);
08046 
08047    return res;
08048 }
08049 
08050 static int reload(void)
08051 {
08052    ast_unload_realtime("meetme");
08053    return load_config(1);
08054 }
08055 
08056 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "MeetMe conference bridge",
08057       .load = load_module,
08058       .unload = unload_module,
08059       .reload = reload,
08060       .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
08061           );
08062