Mon Oct 8 12:38:55 2012

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

Generated on Mon Oct 8 12:38:56 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7