Mon Mar 19 11:30:20 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: 350023 $")
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: %i\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: %i\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: %i\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: %i\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                            ast_moh_start(chan, NULL, NULL);
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                      ast_moh_start(chan, NULL, NULL);
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    sscanf(callerident, "%30i", &cid);
04468    if (conf && callerident) {
04469       user = ao2_find(conf->usercontainer, &cid, 0);
04470       /* reference decremented later in admin_exec */
04471       return user;
04472    }
04473    return NULL;
04474 }
04475 
04476 static int user_listen_volup_cb(void *obj, void *unused, int flags)
04477 {
04478    struct ast_conf_user *user = obj;
04479    tweak_listen_volume(user, VOL_UP);
04480    return 0;
04481 }
04482 
04483 static int user_listen_voldown_cb(void *obj, void *unused, int flags)
04484 {
04485    struct ast_conf_user *user = obj;
04486    tweak_listen_volume(user, VOL_DOWN);
04487    return 0;
04488 }
04489 
04490 static int user_talk_volup_cb(void *obj, void *unused, int flags)
04491 {
04492    struct ast_conf_user *user = obj;
04493    tweak_talk_volume(user, VOL_UP);
04494    return 0;
04495 }
04496 
04497 static int user_talk_voldown_cb(void *obj, void *unused, int flags)
04498 {
04499    struct ast_conf_user *user = obj;
04500    tweak_talk_volume(user, VOL_DOWN);
04501    return 0;
04502 }
04503 
04504 static int user_reset_vol_cb(void *obj, void *unused, int flags)
04505 {
04506    struct ast_conf_user *user = obj;
04507    reset_volumes(user);
04508    return 0;
04509 }
04510 
04511 static int user_chan_cb(void *obj, void *args, int flags)
04512 {
04513    struct ast_conf_user *user = obj;
04514    const char *channel = args;
04515 
04516    if (!strcmp(user->chan->name, channel)) {
04517       return (CMP_MATCH | CMP_STOP);
04518    }
04519 
04520    return 0;
04521 }
04522 
04523 /*! \brief The MeetMeadmin application 
04524 
04525   MeetMeAdmin(confno, command, caller) */
04526 static int admin_exec(struct ast_channel *chan, const char *data) {
04527    char *params;
04528    struct ast_conference *cnf;
04529    struct ast_conf_user *user = NULL;
04530    AST_DECLARE_APP_ARGS(args,
04531       AST_APP_ARG(confno);
04532       AST_APP_ARG(command);
04533       AST_APP_ARG(user);
04534    );
04535    int res = 0;
04536 
04537    if (ast_strlen_zero(data)) {
04538       ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
04539       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
04540       return -1;
04541    }
04542 
04543    params = ast_strdupa(data);
04544    AST_STANDARD_APP_ARGS(args, params);
04545 
04546    if (!args.command) {
04547       ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
04548       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
04549       return -1;
04550    }
04551 
04552    AST_LIST_LOCK(&confs);
04553    AST_LIST_TRAVERSE(&confs, cnf, list) {
04554       if (!strcmp(cnf->confno, args.confno))
04555          break;
04556    }
04557 
04558    if (!cnf) {
04559       ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
04560       AST_LIST_UNLOCK(&confs);
04561       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOTFOUND");
04562       return 0;
04563    }
04564 
04565    ast_atomic_fetchadd_int(&cnf->refcount, 1);
04566 
04567    if (args.user) {
04568       user = find_user(cnf, args.user);
04569       if (!user) {
04570          ast_log(LOG_NOTICE, "Specified User not found!\n");
04571          res = -2;
04572          goto usernotfound;
04573       }
04574    }
04575 
04576    switch (*args.command) {
04577    case 76: /* L: Lock */ 
04578       cnf->locked = 1;
04579       break;
04580    case 108: /* l: Unlock */ 
04581       cnf->locked = 0;
04582       break;
04583    case 75: /* K: kick all users */
04584       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_kickme_cb, NULL);
04585       break;
04586    case 101: /* e: Eject last user*/
04587    {
04588       int max_no = 0;
04589       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
04590       user = ao2_find(cnf->usercontainer, &max_no, 0);
04591       if (!ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))
04592          user->adminflags |= ADMINFLAG_KICKME;
04593       else {
04594          res = -1;
04595          ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
04596       }
04597       ao2_ref(user, -1);
04598       break;
04599    }
04600    case 77: /* M: Mute */ 
04601       user->adminflags |= ADMINFLAG_MUTED;
04602       break;
04603    case 78: /* N: Mute all (non-admin) users */
04604       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_muted_cb, NULL);
04605       break;               
04606    case 109: /* m: Unmute */ 
04607       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
04608       break;
04609    case 110: /* n: Unmute all users */
04610       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, NULL);
04611       break;
04612    case 107: /* k: Kick user */ 
04613       user->adminflags |= ADMINFLAG_KICKME;
04614       break;
04615    case 118: /* v: Lower all users listen volume */
04616       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_voldown_cb, NULL);
04617       break;
04618    case 86: /* V: Raise all users listen volume */
04619       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_volup_cb, NULL);
04620       break;
04621    case 115: /* s: Lower all users speaking volume */
04622       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_voldown_cb, NULL);
04623       break;
04624    case 83: /* S: Raise all users speaking volume */
04625       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_volup_cb, NULL);
04626       break;
04627    case 82: /* R: Reset all volume levels */
04628       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_reset_vol_cb, NULL);
04629       break;
04630    case 114: /* r: Reset user's volume level */
04631       reset_volumes(user);
04632       break;
04633    case 85: /* U: Raise user's listen volume */
04634       tweak_listen_volume(user, VOL_UP);
04635       break;
04636    case 117: /* u: Lower user's listen volume */
04637       tweak_listen_volume(user, VOL_DOWN);
04638       break;
04639    case 84: /* T: Raise user's talk volume */
04640       tweak_talk_volume(user, VOL_UP);
04641       break;
04642    case 116: /* t: Lower user's talk volume */
04643       tweak_talk_volume(user, VOL_DOWN);
04644       break;
04645    case 'E': /* E: Extend conference */
04646       if (rt_extend_conf(args.confno)) {
04647          res = -1;
04648       }
04649       break;
04650    }
04651 
04652    if (args.user) {
04653       /* decrement reference from find_user */
04654       ao2_ref(user, -1);
04655    }
04656 usernotfound:
04657    AST_LIST_UNLOCK(&confs);
04658 
04659    dispose_conf(cnf);
04660    pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", res == -2 ? "NOTFOUND" : res ? "FAILED" : "OK");
04661 
04662    return 0;
04663 }
04664 
04665 /*! \brief The MeetMeChannelAdmin application 
04666    MeetMeChannelAdmin(channel, command) */
04667 static int channel_admin_exec(struct ast_channel *chan, const char *data) {
04668    char *params;
04669    struct ast_conference *conf = NULL;
04670    struct ast_conf_user *user = NULL;
04671    AST_DECLARE_APP_ARGS(args,
04672       AST_APP_ARG(channel);
04673       AST_APP_ARG(command);
04674    );
04675 
04676    if (ast_strlen_zero(data)) {
04677       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
04678       return -1;
04679    }
04680    
04681    params = ast_strdupa(data);
04682    AST_STANDARD_APP_ARGS(args, params);
04683 
04684    if (!args.channel) {
04685       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
04686       return -1;
04687    }
04688 
04689    if (!args.command) {
04690       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
04691       return -1;
04692    }
04693 
04694    AST_LIST_LOCK(&confs);
04695    AST_LIST_TRAVERSE(&confs, conf, list) {
04696       if ((user = ao2_callback(conf->usercontainer, 0, user_chan_cb, args.channel))) {
04697          break;
04698       }
04699    }
04700    
04701    if (!user) {
04702       ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
04703       AST_LIST_UNLOCK(&confs);
04704       return 0;
04705    }
04706    
04707    /* perform the specified action */
04708    switch (*args.command) {
04709       case 77: /* M: Mute */ 
04710          user->adminflags |= ADMINFLAG_MUTED;
04711          break;
04712       case 109: /* m: Unmute */ 
04713          user->adminflags &= ~ADMINFLAG_MUTED;
04714          break;
04715       case 107: /* k: Kick user */ 
04716          user->adminflags |= ADMINFLAG_KICKME;
04717          break;
04718       default: /* unknown command */
04719          ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
04720          break;
04721    }
04722    ao2_ref(user, -1);
04723    AST_LIST_UNLOCK(&confs);
04724    
04725    return 0;
04726 }
04727 
04728 static int meetmemute(struct mansession *s, const struct message *m, int mute)
04729 {
04730    struct ast_conference *conf;
04731    struct ast_conf_user *user;
04732    const char *confid = astman_get_header(m, "Meetme");
04733    char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
04734    int userno;
04735 
04736    if (ast_strlen_zero(confid)) {
04737       astman_send_error(s, m, "Meetme conference not specified");
04738       return 0;
04739    }
04740 
04741    if (ast_strlen_zero(userid)) {
04742       astman_send_error(s, m, "Meetme user number not specified");
04743       return 0;
04744    }
04745 
04746    userno = strtoul(userid, &userid, 10);
04747 
04748    if (*userid) {
04749       astman_send_error(s, m, "Invalid user number");
04750       return 0;
04751    }
04752 
04753    /* Look in the conference list */
04754    AST_LIST_LOCK(&confs);
04755    AST_LIST_TRAVERSE(&confs, conf, list) {
04756       if (!strcmp(confid, conf->confno))
04757          break;
04758    }
04759 
04760    if (!conf) {
04761       AST_LIST_UNLOCK(&confs);
04762       astman_send_error(s, m, "Meetme conference does not exist");
04763       return 0;
04764    }
04765 
04766    user = ao2_find(conf->usercontainer, &userno, 0);
04767 
04768    if (!user) {
04769       AST_LIST_UNLOCK(&confs);
04770       astman_send_error(s, m, "User number not found");
04771       return 0;
04772    }
04773 
04774    if (mute)
04775       user->adminflags |= ADMINFLAG_MUTED;   /* request user muting */
04776    else
04777       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST); /* request user unmuting */
04778 
04779    AST_LIST_UNLOCK(&confs);
04780 
04781    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);
04782 
04783    ao2_ref(user, -1);
04784    astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
04785    return 0;
04786 }
04787 
04788 static int action_meetmemute(struct mansession *s, const struct message *m)
04789 {
04790    return meetmemute(s, m, 1);
04791 }
04792 
04793 static int action_meetmeunmute(struct mansession *s, const struct message *m)
04794 {
04795    return meetmemute(s, m, 0);
04796 }
04797 
04798 static int action_meetmelist(struct mansession *s, const struct message *m)
04799 {
04800    const char *actionid = astman_get_header(m, "ActionID");
04801    const char *conference = astman_get_header(m, "Conference");
04802    char idText[80] = "";
04803    struct ast_conference *cnf;
04804    struct ast_conf_user *user;
04805    struct ao2_iterator user_iter;
04806    int total = 0;
04807 
04808    if (!ast_strlen_zero(actionid))
04809       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
04810 
04811    if (AST_LIST_EMPTY(&confs)) {
04812       astman_send_error(s, m, "No active conferences.");
04813       return 0;
04814    }
04815 
04816    astman_send_listack(s, m, "Meetme user list will follow", "start");
04817 
04818    /* Find the right conference */
04819    AST_LIST_LOCK(&confs);
04820    AST_LIST_TRAVERSE(&confs, cnf, list) {
04821       /* If we ask for one particular, and this isn't it, skip it */
04822       if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
04823          continue;
04824 
04825       /* Show all the users */
04826       user_iter = ao2_iterator_init(cnf->usercontainer, 0);
04827       while ((user = ao2_iterator_next(&user_iter))) {
04828          total++;
04829          astman_append(s,
04830             "Event: MeetmeList\r\n"
04831             "%s"
04832             "Conference: %s\r\n"
04833             "UserNumber: %d\r\n"
04834             "CallerIDNum: %s\r\n"
04835             "CallerIDName: %s\r\n"
04836             "ConnectedLineNum: %s\r\n"
04837             "ConnectedLineName: %s\r\n"
04838             "Channel: %s\r\n"
04839             "Admin: %s\r\n"
04840             "Role: %s\r\n"
04841             "MarkedUser: %s\r\n"
04842             "Muted: %s\r\n"
04843             "Talking: %s\r\n"
04844             "\r\n",
04845             idText,
04846             cnf->confno,
04847             user->user_no,
04848             S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
04849             S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<no name>"),
04850             S_COR(user->chan->connected.id.number.valid, user->chan->connected.id.number.str, "<unknown>"),
04851             S_COR(user->chan->connected.id.name.valid, user->chan->connected.id.name.str, "<no name>"),
04852             user->chan->name,
04853             ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "Yes" : "No",
04854             ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "Listen only" : ast_test_flag64(&user->userflags, CONFFLAG_TALKER) ? "Talk only" : "Talk and listen",
04855             ast_test_flag64(&user->userflags, CONFFLAG_MARKEDUSER) ? "Yes" : "No",
04856             user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
04857             user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
04858          ao2_ref(user, -1);
04859       }
04860       ao2_iterator_destroy(&user_iter);
04861    }
04862    AST_LIST_UNLOCK(&confs);
04863    /* Send final confirmation */
04864    astman_append(s,
04865    "Event: MeetmeListComplete\r\n"
04866    "EventList: Complete\r\n"
04867    "ListItems: %d\r\n"
04868    "%s"
04869    "\r\n", total, idText);
04870    return 0;
04871 }
04872 
04873 static void *recordthread(void *args)
04874 {
04875    struct ast_conference *cnf = args;
04876    struct ast_frame *f = NULL;
04877    int flags;
04878    struct ast_filestream *s = NULL;
04879    int res = 0;
04880    int x;
04881    const char *oldrecordingfilename = NULL;
04882 
04883    if (!cnf || !cnf->lchan) {
04884       pthread_exit(0);
04885    }
04886 
04887    ast_stopstream(cnf->lchan);
04888    flags = O_CREAT | O_TRUNC | O_WRONLY;
04889 
04890 
04891    cnf->recording = MEETME_RECORD_ACTIVE;
04892    while (ast_waitfor(cnf->lchan, -1) > -1) {
04893       if (cnf->recording == MEETME_RECORD_TERMINATE) {
04894          AST_LIST_LOCK(&confs);
04895          AST_LIST_UNLOCK(&confs);
04896          break;
04897       }
04898       if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
04899          s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
04900          oldrecordingfilename = cnf->recordingfilename;
04901       }
04902       
04903       f = ast_read(cnf->lchan);
04904       if (!f) {
04905          res = -1;
04906          break;
04907       }
04908       if (f->frametype == AST_FRAME_VOICE) {
04909          ast_mutex_lock(&cnf->listenlock);
04910          for (x = 0; x < AST_FRAME_BITS; x++) {
04911             /* Free any translations that have occured */
04912             if (cnf->transframe[x]) {
04913                ast_frfree(cnf->transframe[x]);
04914                cnf->transframe[x] = NULL;
04915             }
04916          }
04917          if (cnf->origframe)
04918             ast_frfree(cnf->origframe);
04919          cnf->origframe = ast_frdup(f);
04920          ast_mutex_unlock(&cnf->listenlock);
04921          if (s)
04922             res = ast_writestream(s, f);
04923          if (res) {
04924             ast_frfree(f);
04925             break;
04926          }
04927       }
04928       ast_frfree(f);
04929    }
04930    cnf->recording = MEETME_RECORD_OFF;
04931    if (s)
04932       ast_closestream(s);
04933    
04934    pthread_exit(0);
04935 }
04936 
04937 /*! \brief Callback for devicestate providers */
04938 static enum ast_device_state meetmestate(const char *data)
04939 {
04940    struct ast_conference *conf;
04941 
04942    /* Find conference */
04943    AST_LIST_LOCK(&confs);
04944    AST_LIST_TRAVERSE(&confs, conf, list) {
04945       if (!strcmp(data, conf->confno))
04946          break;
04947    }
04948    AST_LIST_UNLOCK(&confs);
04949    if (!conf)
04950       return AST_DEVICE_INVALID;
04951 
04952 
04953    /* SKREP to fill */
04954    if (!conf->users)
04955       return AST_DEVICE_NOT_INUSE;
04956 
04957    return AST_DEVICE_INUSE;
04958 }
04959 
04960 static void load_config_meetme(void)
04961 {
04962    struct ast_config *cfg;
04963    struct ast_flags config_flags = { 0 };
04964    const char *val;
04965 
04966    if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags))) {
04967       return;
04968    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
04969       ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format.  Aborting.\n");
04970       return;
04971    }
04972 
04973    audio_buffers = DEFAULT_AUDIO_BUFFERS;
04974 
04975    /*  Scheduling support is off by default */
04976    rt_schedule = 0;
04977    fuzzystart = 0;
04978    earlyalert = 0;
04979    endalert = 0;
04980    extendby = 0;
04981 
04982    /*  Logging of participants defaults to ON for compatibility reasons */
04983    rt_log_members = 1;  
04984 
04985    if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
04986       if ((sscanf(val, "%30d", &audio_buffers) != 1)) {
04987          ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
04988          audio_buffers = DEFAULT_AUDIO_BUFFERS;
04989       } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
04990          ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
04991             DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
04992          audio_buffers = DEFAULT_AUDIO_BUFFERS;
04993       }
04994       if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
04995          ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
04996    }
04997 
04998    if ((val = ast_variable_retrieve(cfg, "general", "schedule")))
04999       rt_schedule = ast_true(val);
05000    if ((val = ast_variable_retrieve(cfg, "general", "logmembercount")))
05001       rt_log_members = ast_true(val);
05002    if ((val = ast_variable_retrieve(cfg, "general", "fuzzystart"))) {
05003       if ((sscanf(val, "%30d", &fuzzystart) != 1)) {
05004          ast_log(LOG_WARNING, "fuzzystart must be a number, not '%s'\n", val);
05005          fuzzystart = 0;
05006       } 
05007    }
05008    if ((val = ast_variable_retrieve(cfg, "general", "earlyalert"))) {
05009       if ((sscanf(val, "%30d", &earlyalert) != 1)) {
05010          ast_log(LOG_WARNING, "earlyalert must be a number, not '%s'\n", val);
05011          earlyalert = 0;
05012       } 
05013    }
05014    if ((val = ast_variable_retrieve(cfg, "general", "endalert"))) {
05015       if ((sscanf(val, "%30d", &endalert) != 1)) {
05016          ast_log(LOG_WARNING, "endalert must be a number, not '%s'\n", val);
05017          endalert = 0;
05018       } 
05019    }
05020    if ((val = ast_variable_retrieve(cfg, "general", "extendby"))) {
05021       if ((sscanf(val, "%30d", &extendby) != 1)) {
05022          ast_log(LOG_WARNING, "extendby must be a number, not '%s'\n", val);
05023          extendby = 0;
05024       } 
05025    }
05026 
05027    ast_config_destroy(cfg);
05028 }
05029 
05030 /*! \brief Find an SLA trunk by name
05031  * \note This must be called with the sla_trunks container locked
05032  */
05033 static struct sla_trunk *sla_find_trunk(const char *name)
05034 {
05035    struct sla_trunk *trunk = NULL;
05036 
05037    AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
05038       if (!strcasecmp(trunk->name, name))
05039          break;
05040    }
05041 
05042    return trunk;
05043 }
05044 
05045 /*! \brief Find an SLA station by name
05046  * \note This must be called with the sla_stations container locked
05047  */
05048 static struct sla_station *sla_find_station(const char *name)
05049 {
05050    struct sla_station *station = NULL;
05051 
05052    AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
05053       if (!strcasecmp(station->name, name))
05054          break;
05055    }
05056 
05057    return station;
05058 }
05059 
05060 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
05061    const struct sla_station *station)
05062 {
05063    struct sla_station_ref *station_ref;
05064    struct sla_trunk_ref *trunk_ref;
05065 
05066    /* For each station that has this call on hold, check for private hold. */
05067    AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
05068       AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
05069          if (trunk_ref->trunk != trunk || station_ref->station == station)
05070             continue;
05071          if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
05072             station_ref->station->hold_access == SLA_HOLD_PRIVATE)
05073             return 1;
05074          return 0;
05075       }
05076    }
05077 
05078    return 0;
05079 }
05080 
05081 /*! \brief Find a trunk reference on a station by name
05082  * \param station the station
05083  * \param name the trunk's name
05084  * \return a pointer to the station's trunk reference.  If the trunk
05085  *         is not found, it is not idle and barge is disabled, or if
05086  *         it is on hold and private hold is set, then NULL will be returned.
05087  */
05088 static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
05089    const char *name)
05090 {
05091    struct sla_trunk_ref *trunk_ref = NULL;
05092 
05093    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05094       if (strcasecmp(trunk_ref->trunk->name, name))
05095          continue;
05096 
05097       if ( (trunk_ref->trunk->barge_disabled 
05098          && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
05099          (trunk_ref->trunk->hold_stations 
05100          && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
05101          && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
05102          sla_check_station_hold_access(trunk_ref->trunk, station) ) 
05103       {
05104          trunk_ref = NULL;
05105       }
05106 
05107       break;
05108    }
05109 
05110    return trunk_ref;
05111 }
05112 
05113 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
05114 {
05115    struct sla_station_ref *station_ref;
05116 
05117    if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
05118       return NULL;
05119 
05120    station_ref->station = station;
05121 
05122    return station_ref;
05123 }
05124 
05125 static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
05126 {
05127    struct sla_ringing_station *ringing_station;
05128 
05129    if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
05130       return NULL;
05131 
05132    ringing_station->station = station;
05133    ringing_station->ring_begin = ast_tvnow();
05134 
05135    return ringing_station;
05136 }
05137 
05138 static enum ast_device_state sla_state_to_devstate(enum sla_trunk_state state)
05139 {
05140    switch (state) {
05141    case SLA_TRUNK_STATE_IDLE:
05142       return AST_DEVICE_NOT_INUSE;
05143    case SLA_TRUNK_STATE_RINGING:
05144       return AST_DEVICE_RINGING;
05145    case SLA_TRUNK_STATE_UP:
05146       return AST_DEVICE_INUSE;
05147    case SLA_TRUNK_STATE_ONHOLD:
05148    case SLA_TRUNK_STATE_ONHOLD_BYME:
05149       return AST_DEVICE_ONHOLD;
05150    }
05151 
05152    return AST_DEVICE_UNKNOWN;
05153 }
05154 
05155 static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state, 
05156    enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
05157 {
05158    struct sla_station *station;
05159    struct sla_trunk_ref *trunk_ref;
05160 
05161    AST_LIST_TRAVERSE(&sla_stations, station, entry) {
05162       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05163          if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
05164             || trunk_ref == exclude)
05165             continue;
05166          trunk_ref->state = state;
05167          ast_devstate_changed(sla_state_to_devstate(state), 
05168             "SLA:%s_%s", station->name, trunk->name);
05169          break;
05170       }
05171    }
05172 }
05173 
05174 struct run_station_args {
05175    struct sla_station *station;
05176    struct sla_trunk_ref *trunk_ref;
05177    ast_mutex_t *cond_lock;
05178    ast_cond_t *cond;
05179 };
05180 
05181 static void answer_trunk_chan(struct ast_channel *chan)
05182 {
05183    ast_answer(chan);
05184    ast_indicate(chan, -1);
05185 }
05186 
05187 static void *run_station(void *data)
05188 {
05189    struct sla_station *station;
05190    struct sla_trunk_ref *trunk_ref;
05191    struct ast_str *conf_name = ast_str_create(16);
05192    struct ast_flags64 conf_flags = { 0 };
05193    struct ast_conference *conf;
05194 
05195    {
05196       struct run_station_args *args = data;
05197       station = args->station;
05198       trunk_ref = args->trunk_ref;
05199       ast_mutex_lock(args->cond_lock);
05200       ast_cond_signal(args->cond);
05201       ast_mutex_unlock(args->cond_lock);
05202       /* args is no longer valid here. */
05203    }
05204 
05205    ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
05206    ast_str_set(&conf_name, 0, "SLA_%s", trunk_ref->trunk->name);
05207    ast_set_flag64(&conf_flags, 
05208       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
05209    answer_trunk_chan(trunk_ref->chan);
05210    conf = build_conf(ast_str_buffer(conf_name), "", "", 0, 0, 1, trunk_ref->chan, NULL);
05211    if (conf) {
05212       conf_run(trunk_ref->chan, conf, &conf_flags, NULL);
05213       dispose_conf(conf);
05214       conf = NULL;
05215    }
05216    trunk_ref->chan = NULL;
05217    if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
05218       trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
05219       ast_str_append(&conf_name, 0, ",K");
05220       admin_exec(NULL, ast_str_buffer(conf_name));
05221       trunk_ref->trunk->hold_stations = 0;
05222       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05223    }
05224 
05225    ast_dial_join(station->dial);
05226    ast_dial_destroy(station->dial);
05227    station->dial = NULL;
05228    ast_free(conf_name);
05229 
05230    return NULL;
05231 }
05232 
05233 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
05234 {
05235    char buf[80];
05236    struct sla_station_ref *station_ref;
05237 
05238    snprintf(buf, sizeof(buf), "SLA_%s,K", ringing_trunk->trunk->name);
05239    admin_exec(NULL, buf);
05240    sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05241 
05242    while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry)))
05243       ast_free(station_ref);
05244 
05245    ast_free(ringing_trunk);
05246 }
05247 
05248 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
05249    enum sla_station_hangup hangup)
05250 {
05251    struct sla_ringing_trunk *ringing_trunk;
05252    struct sla_trunk_ref *trunk_ref;
05253    struct sla_station_ref *station_ref;
05254 
05255    ast_dial_join(ringing_station->station->dial);
05256    ast_dial_destroy(ringing_station->station->dial);
05257    ringing_station->station->dial = NULL;
05258 
05259    if (hangup == SLA_STATION_HANGUP_NORMAL)
05260       goto done;
05261 
05262    /* If the station is being hung up because of a timeout, then add it to the
05263     * list of timed out stations on each of the ringing trunks.  This is so
05264     * that when doing further processing to figure out which stations should be
05265     * ringing, which trunk to answer, determining timeouts, etc., we know which
05266     * ringing trunks we should ignore. */
05267    AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05268       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
05269          if (ringing_trunk->trunk == trunk_ref->trunk)
05270             break;
05271       }
05272       if (!trunk_ref)
05273          continue;
05274       if (!(station_ref = sla_create_station_ref(ringing_station->station)))
05275          continue;
05276       AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
05277    }
05278 
05279 done:
05280    ast_free(ringing_station);
05281 }
05282 
05283 static void sla_dial_state_callback(struct ast_dial *dial)
05284 {
05285    sla_queue_event(SLA_EVENT_DIAL_STATE);
05286 }
05287 
05288 /*! \brief Check to see if dialing this station already timed out for this ringing trunk
05289  * \note Assumes sla.lock is locked
05290  */
05291 static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
05292    const struct sla_station *station)
05293 {
05294    struct sla_station_ref *timed_out_station;
05295 
05296    AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
05297       if (station == timed_out_station->station)
05298          return 1;
05299    }
05300 
05301    return 0;
05302 }
05303 
05304 /*! \brief Choose the highest priority ringing trunk for a station
05305  * \param station the station
05306  * \param rm remove the ringing trunk once selected
05307  * \param trunk_ref a place to store the pointer to this stations reference to
05308  *        the selected trunk
05309  * \return a pointer to the selected ringing trunk, or NULL if none found
05310  * \note Assumes that sla.lock is locked
05311  */
05312 static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station, 
05313    struct sla_trunk_ref **trunk_ref, int rm)
05314 {
05315    struct sla_trunk_ref *s_trunk_ref;
05316    struct sla_ringing_trunk *ringing_trunk = NULL;
05317 
05318    AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
05319       AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
05320          /* Make sure this is the trunk we're looking for */
05321          if (s_trunk_ref->trunk != ringing_trunk->trunk)
05322             continue;
05323 
05324          /* This trunk on the station is ringing.  But, make sure this station
05325           * didn't already time out while this trunk was ringing. */
05326          if (sla_check_timed_out_station(ringing_trunk, station))
05327             continue;
05328 
05329          if (rm)
05330             AST_LIST_REMOVE_CURRENT(entry);
05331 
05332          if (trunk_ref)
05333             *trunk_ref = s_trunk_ref;
05334 
05335          break;
05336       }
05337       AST_LIST_TRAVERSE_SAFE_END;
05338    
05339       if (ringing_trunk)
05340          break;
05341    }
05342 
05343    return ringing_trunk;
05344 }
05345 
05346 static void sla_handle_dial_state_event(void)
05347 {
05348    struct sla_ringing_station *ringing_station;
05349 
05350    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
05351       struct sla_trunk_ref *s_trunk_ref = NULL;
05352       struct sla_ringing_trunk *ringing_trunk = NULL;
05353       struct run_station_args args;
05354       enum ast_dial_result dial_res;
05355       pthread_t dont_care;
05356       ast_mutex_t cond_lock;
05357       ast_cond_t cond;
05358 
05359       switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
05360       case AST_DIAL_RESULT_HANGUP:
05361       case AST_DIAL_RESULT_INVALID:
05362       case AST_DIAL_RESULT_FAILED:
05363       case AST_DIAL_RESULT_TIMEOUT:
05364       case AST_DIAL_RESULT_UNANSWERED:
05365          AST_LIST_REMOVE_CURRENT(entry);
05366          sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
05367          break;
05368       case AST_DIAL_RESULT_ANSWERED:
05369          AST_LIST_REMOVE_CURRENT(entry);
05370          /* Find the appropriate trunk to answer. */
05371          ast_mutex_lock(&sla.lock);
05372          ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
05373          ast_mutex_unlock(&sla.lock);
05374          if (!ringing_trunk) {
05375             ast_debug(1, "Found no ringing trunk for station '%s' to answer!\n", ringing_station->station->name);
05376             break;
05377          }
05378          /* Track the channel that answered this trunk */
05379          s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
05380          /* Actually answer the trunk */
05381          answer_trunk_chan(ringing_trunk->trunk->chan);
05382          sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
05383          /* Now, start a thread that will connect this station to the trunk.  The rest of
05384           * the code here sets up the thread and ensures that it is able to save the arguments
05385           * before they are no longer valid since they are allocated on the stack. */
05386          args.trunk_ref = s_trunk_ref;
05387          args.station = ringing_station->station;
05388          args.cond = &cond;
05389          args.cond_lock = &cond_lock;
05390          ast_free(ringing_trunk);
05391          ast_free(ringing_station);
05392          ast_mutex_init(&cond_lock);
05393          ast_cond_init(&cond, NULL);
05394          ast_mutex_lock(&cond_lock);
05395          ast_pthread_create_detached_background(&dont_care, NULL, run_station, &args);
05396          ast_cond_wait(&cond, &cond_lock);
05397          ast_mutex_unlock(&cond_lock);
05398          ast_mutex_destroy(&cond_lock);
05399          ast_cond_destroy(&cond);
05400          break;
05401       case AST_DIAL_RESULT_TRYING:
05402       case AST_DIAL_RESULT_RINGING:
05403       case AST_DIAL_RESULT_PROGRESS:
05404       case AST_DIAL_RESULT_PROCEEDING:
05405          break;
05406       }
05407       if (dial_res == AST_DIAL_RESULT_ANSWERED) {
05408          /* Queue up reprocessing ringing trunks, and then ringing stations again */
05409          sla_queue_event(SLA_EVENT_RINGING_TRUNK);
05410          sla_queue_event(SLA_EVENT_DIAL_STATE);
05411          break;
05412       }
05413    }
05414    AST_LIST_TRAVERSE_SAFE_END;
05415 }
05416 
05417 /*! \brief Check to see if this station is already ringing 
05418  * \note Assumes sla.lock is locked 
05419  */
05420 static int sla_check_ringing_station(const struct sla_station *station)
05421 {
05422    struct sla_ringing_station *ringing_station;
05423 
05424    AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
05425       if (station == ringing_station->station)
05426          return 1;
05427    }
05428 
05429    return 0;
05430 }
05431 
05432 /*! \brief Check to see if this station has failed to be dialed in the past minute
05433  * \note assumes sla.lock is locked
05434  */
05435 static int sla_check_failed_station(const struct sla_station *station)
05436 {
05437    struct sla_failed_station *failed_station;
05438    int res = 0;
05439 
05440    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
05441       if (station != failed_station->station)
05442          continue;
05443       if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
05444          AST_LIST_REMOVE_CURRENT(entry);
05445          ast_free(failed_station);
05446          break;
05447       }
05448       res = 1;
05449    }
05450    AST_LIST_TRAVERSE_SAFE_END
05451 
05452    return res;
05453 }
05454 
05455 /*! \brief Ring a station
05456  * \note Assumes sla.lock is locked
05457  */
05458 static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
05459 {
05460    char *tech, *tech_data;
05461    struct ast_dial *dial;
05462    struct sla_ringing_station *ringing_station;
05463    enum ast_dial_result res;
05464    int caller_is_saved;
05465    struct ast_party_caller caller;
05466 
05467    if (!(dial = ast_dial_create()))
05468       return -1;
05469 
05470    ast_dial_set_state_callback(dial, sla_dial_state_callback);
05471    tech_data = ast_strdupa(station->device);
05472    tech = strsep(&tech_data, "/");
05473 
05474    if (ast_dial_append(dial, tech, tech_data) == -1) {
05475       ast_dial_destroy(dial);
05476       return -1;
05477    }
05478 
05479    /* Do we need to save off the caller ID data? */
05480    caller_is_saved = 0;
05481    if (!sla.attempt_callerid) {
05482       caller_is_saved = 1;
05483       caller = ringing_trunk->trunk->chan->caller;
05484       ast_party_caller_init(&ringing_trunk->trunk->chan->caller);
05485    }
05486 
05487    res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
05488    
05489    /* Restore saved caller ID */
05490    if (caller_is_saved) {
05491       ast_party_caller_free(&ringing_trunk->trunk->chan->caller);
05492       ringing_trunk->trunk->chan->caller = caller;
05493    }
05494    
05495    if (res != AST_DIAL_RESULT_TRYING) {
05496       struct sla_failed_station *failed_station;
05497       ast_dial_destroy(dial);
05498       if (!(failed_station = ast_calloc(1, sizeof(*failed_station))))
05499          return -1;
05500       failed_station->station = station;
05501       failed_station->last_try = ast_tvnow();
05502       AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
05503       return -1;
05504    }
05505    if (!(ringing_station = sla_create_ringing_station(station))) {
05506       ast_dial_join(dial);
05507       ast_dial_destroy(dial);
05508       return -1;
05509    }
05510 
05511    station->dial = dial;
05512 
05513    AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
05514 
05515    return 0;
05516 }
05517 
05518 /*! \brief Check to see if a station is in use
05519  */
05520 static int sla_check_inuse_station(const struct sla_station *station)
05521 {
05522    struct sla_trunk_ref *trunk_ref;
05523 
05524    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05525       if (trunk_ref->chan)
05526          return 1;
05527    }
05528 
05529    return 0;
05530 }
05531 
05532 static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
05533    const struct sla_trunk *trunk)
05534 {
05535    struct sla_trunk_ref *trunk_ref = NULL;
05536 
05537    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05538       if (trunk_ref->trunk == trunk)
05539          break;
05540    }
05541 
05542    return trunk_ref;
05543 }
05544 
05545 /*! \brief Calculate the ring delay for a given ringing trunk on a station
05546  * \param station the station
05547  * \param ringing_trunk the trunk.  If NULL, the highest priority ringing trunk will be used
05548  * \return the number of ms left before the delay is complete, or INT_MAX if there is no delay
05549  */
05550 static int sla_check_station_delay(struct sla_station *station, 
05551    struct sla_ringing_trunk *ringing_trunk)
05552 {
05553    struct sla_trunk_ref *trunk_ref;
05554    unsigned int delay = UINT_MAX;
05555    int time_left, time_elapsed;
05556 
05557    if (!ringing_trunk)
05558       ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
05559    else
05560       trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
05561 
05562    if (!ringing_trunk || !trunk_ref)
05563       return delay;
05564 
05565    /* If this station has a ring delay specific to the highest priority
05566     * ringing trunk, use that.  Otherwise, use the ring delay specified
05567     * globally for the station. */
05568    delay = trunk_ref->ring_delay;
05569    if (!delay)
05570       delay = station->ring_delay;
05571    if (!delay)
05572       return INT_MAX;
05573 
05574    time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
05575    time_left = (delay * 1000) - time_elapsed;
05576 
05577    return time_left;
05578 }
05579 
05580 /*! \brief Ring stations based on current set of ringing trunks
05581  * \note Assumes that sla.lock is locked
05582  */
05583 static void sla_ring_stations(void)
05584 {
05585    struct sla_station_ref *station_ref;
05586    struct sla_ringing_trunk *ringing_trunk;
05587 
05588    /* Make sure that every station that uses at least one of the ringing
05589     * trunks, is ringing. */
05590    AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05591       AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
05592          int time_left;
05593 
05594          /* Is this station already ringing? */
05595          if (sla_check_ringing_station(station_ref->station))
05596             continue;
05597 
05598          /* Is this station already in a call? */
05599          if (sla_check_inuse_station(station_ref->station))
05600             continue;
05601 
05602          /* Did we fail to dial this station earlier?  If so, has it been
05603           * a minute since we tried? */
05604          if (sla_check_failed_station(station_ref->station))
05605             continue;
05606 
05607          /* If this station already timed out while this trunk was ringing,
05608           * do not dial it again for this ringing trunk. */
05609          if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
05610             continue;
05611 
05612          /* Check for a ring delay in progress */
05613          time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
05614          if (time_left != INT_MAX && time_left > 0)
05615             continue;
05616 
05617          /* It is time to make this station begin to ring.  Do it! */
05618          sla_ring_station(ringing_trunk, station_ref->station);
05619       }
05620    }
05621    /* Now, all of the stations that should be ringing, are ringing. */
05622 }
05623 
05624 static void sla_hangup_stations(void)
05625 {
05626    struct sla_trunk_ref *trunk_ref;
05627    struct sla_ringing_station *ringing_station;
05628 
05629    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
05630       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
05631          struct sla_ringing_trunk *ringing_trunk;
05632          ast_mutex_lock(&sla.lock);
05633          AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05634             if (trunk_ref->trunk == ringing_trunk->trunk)
05635                break;
05636          }
05637          ast_mutex_unlock(&sla.lock);
05638          if (ringing_trunk)
05639             break;
05640       }
05641       if (!trunk_ref) {
05642          AST_LIST_REMOVE_CURRENT(entry);
05643          ast_dial_join(ringing_station->station->dial);
05644          ast_dial_destroy(ringing_station->station->dial);
05645          ringing_station->station->dial = NULL;
05646          ast_free(ringing_station);
05647       }
05648    }
05649    AST_LIST_TRAVERSE_SAFE_END
05650 }
05651 
05652 static void sla_handle_ringing_trunk_event(void)
05653 {
05654    ast_mutex_lock(&sla.lock);
05655    sla_ring_stations();
05656    ast_mutex_unlock(&sla.lock);
05657 
05658    /* Find stations that shouldn't be ringing anymore. */
05659    sla_hangup_stations();
05660 }
05661 
05662 static void sla_handle_hold_event(struct sla_event *event)
05663 {
05664    ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
05665    event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
05666    ast_devstate_changed(AST_DEVICE_ONHOLD, "SLA:%s_%s", 
05667       event->station->name, event->trunk_ref->trunk->name);
05668    sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD, 
05669       INACTIVE_TRUNK_REFS, event->trunk_ref);
05670 
05671    if (event->trunk_ref->trunk->active_stations == 1) {
05672       /* The station putting it on hold is the only one on the call, so start
05673        * Music on hold to the trunk. */
05674       event->trunk_ref->trunk->on_hold = 1;
05675       ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
05676    }
05677 
05678    ast_softhangup(event->trunk_ref->chan, AST_SOFTHANGUP_DEV);
05679    event->trunk_ref->chan = NULL;
05680 }
05681 
05682 /*! \brief Process trunk ring timeouts
05683  * \note Called with sla.lock locked
05684  * \return non-zero if a change to the ringing trunks was made
05685  */
05686 static int sla_calc_trunk_timeouts(unsigned int *timeout)
05687 {
05688    struct sla_ringing_trunk *ringing_trunk;
05689    int res = 0;
05690 
05691    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
05692       int time_left, time_elapsed;
05693       if (!ringing_trunk->trunk->ring_timeout)
05694          continue;
05695       time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
05696       time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
05697       if (time_left <= 0) {
05698          pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
05699          AST_LIST_REMOVE_CURRENT(entry);
05700          sla_stop_ringing_trunk(ringing_trunk);
05701          res = 1;
05702          continue;
05703       }
05704       if (time_left < *timeout)
05705          *timeout = time_left;
05706    }
05707    AST_LIST_TRAVERSE_SAFE_END;
05708 
05709    return res;
05710 }
05711 
05712 /*! \brief Process station ring timeouts
05713  * \note Called with sla.lock locked
05714  * \return non-zero if a change to the ringing stations was made
05715  */
05716 static int sla_calc_station_timeouts(unsigned int *timeout)
05717 {
05718    struct sla_ringing_trunk *ringing_trunk;
05719    struct sla_ringing_station *ringing_station;
05720    int res = 0;
05721 
05722    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
05723       unsigned int ring_timeout = 0;
05724       int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
05725       struct sla_trunk_ref *trunk_ref;
05726 
05727       /* If there are any ring timeouts specified for a specific trunk
05728        * on the station, then use the highest per-trunk ring timeout.
05729        * Otherwise, use the ring timeout set for the entire station. */
05730       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
05731          struct sla_station_ref *station_ref;
05732          int trunk_time_elapsed, trunk_time_left;
05733 
05734          AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05735             if (ringing_trunk->trunk == trunk_ref->trunk)
05736                break;
05737          }
05738          if (!ringing_trunk)
05739             continue;
05740 
05741          /* If there is a trunk that is ringing without a timeout, then the
05742           * only timeout that could matter is a global station ring timeout. */
05743          if (!trunk_ref->ring_timeout)
05744             break;
05745 
05746          /* This trunk on this station is ringing and has a timeout.
05747           * However, make sure this trunk isn't still ringing from a
05748           * previous timeout.  If so, don't consider it. */
05749          AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
05750             if (station_ref->station == ringing_station->station)
05751                break;
05752          }
05753          if (station_ref)
05754             continue;
05755 
05756          trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
05757          trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
05758          if (trunk_time_left > final_trunk_time_left)
05759             final_trunk_time_left = trunk_time_left;
05760       }
05761 
05762       /* No timeout was found for ringing trunks, and no timeout for the entire station */
05763       if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
05764          continue;
05765 
05766       /* Compute how much time is left for a global station timeout */
05767       if (ringing_station->station->ring_timeout) {
05768          ring_timeout = ringing_station->station->ring_timeout;
05769          time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
05770          time_left = (ring_timeout * 1000) - time_elapsed;
05771       }
05772 
05773       /* If the time left based on the per-trunk timeouts is smaller than the
05774        * global station ring timeout, use that. */
05775       if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
05776          time_left = final_trunk_time_left;
05777 
05778       /* If there is no time left, the station needs to stop ringing */
05779       if (time_left <= 0) {
05780          AST_LIST_REMOVE_CURRENT(entry);
05781          sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
05782          res = 1;
05783          continue;
05784       }
05785 
05786       /* There is still some time left for this station to ring, so save that
05787        * timeout if it is the first event scheduled to occur */
05788       if (time_left < *timeout)
05789          *timeout = time_left;
05790    }
05791    AST_LIST_TRAVERSE_SAFE_END;
05792 
05793    return res;
05794 }
05795 
05796 /*! \brief Calculate the ring delay for a station
05797  * \note Assumes sla.lock is locked
05798  */
05799 static int sla_calc_station_delays(unsigned int *timeout)
05800 {
05801    struct sla_station *station;
05802    int res = 0;
05803 
05804    AST_LIST_TRAVERSE(&sla_stations, station, entry) {
05805       struct sla_ringing_trunk *ringing_trunk;
05806       int time_left;
05807 
05808       /* Ignore stations already ringing */
05809       if (sla_check_ringing_station(station))
05810          continue;
05811 
05812       /* Ignore stations already on a call */
05813       if (sla_check_inuse_station(station))
05814          continue;
05815 
05816       /* Ignore stations that don't have one of their trunks ringing */
05817       if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
05818          continue;
05819 
05820       if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
05821          continue;
05822 
05823       /* If there is no time left, then the station needs to start ringing.
05824        * Return non-zero so that an event will be queued up an event to 
05825        * make that happen. */
05826       if (time_left <= 0) {
05827          res = 1;
05828          continue;
05829       }
05830 
05831       if (time_left < *timeout)
05832          *timeout = time_left;
05833    }
05834 
05835    return res;
05836 }
05837 
05838 /*! \brief Calculate the time until the next known event
05839  *  \note Called with sla.lock locked */
05840 static int sla_process_timers(struct timespec *ts)
05841 {
05842    unsigned int timeout = UINT_MAX;
05843    struct timeval wait;
05844    unsigned int change_made = 0;
05845 
05846    /* Check for ring timeouts on ringing trunks */
05847    if (sla_calc_trunk_timeouts(&timeout))
05848       change_made = 1;
05849 
05850    /* Check for ring timeouts on ringing stations */
05851    if (sla_calc_station_timeouts(&timeout))
05852       change_made = 1;
05853 
05854    /* Check for station ring delays */
05855    if (sla_calc_station_delays(&timeout))
05856       change_made = 1;
05857 
05858    /* queue reprocessing of ringing trunks */
05859    if (change_made)
05860       sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
05861 
05862    /* No timeout */
05863    if (timeout == UINT_MAX)
05864       return 0;
05865 
05866    if (ts) {
05867       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
05868       ts->tv_sec = wait.tv_sec;
05869       ts->tv_nsec = wait.tv_usec * 1000;
05870    }
05871 
05872    return 1;
05873 }
05874 
05875 static int sla_load_config(int reload);
05876 
05877 /*! \brief Check if we can do a reload of SLA, and do it if we can */
05878 static void sla_check_reload(void)
05879 {
05880    struct sla_station *station;
05881    struct sla_trunk *trunk;
05882 
05883    ast_mutex_lock(&sla.lock);
05884 
05885    if (!AST_LIST_EMPTY(&sla.event_q) || !AST_LIST_EMPTY(&sla.ringing_trunks) 
05886       || !AST_LIST_EMPTY(&sla.ringing_stations)) {
05887       ast_mutex_unlock(&sla.lock);
05888       return;
05889    }
05890 
05891    AST_RWLIST_RDLOCK(&sla_stations);
05892    AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
05893       if (station->ref_count)
05894          break;
05895    }
05896    AST_RWLIST_UNLOCK(&sla_stations);
05897    if (station) {
05898       ast_mutex_unlock(&sla.lock);
05899       return;
05900    }
05901 
05902    AST_RWLIST_RDLOCK(&sla_trunks);
05903    AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
05904       if (trunk->ref_count)
05905          break;
05906    }
05907    AST_RWLIST_UNLOCK(&sla_trunks);
05908    if (trunk) {
05909       ast_mutex_unlock(&sla.lock);
05910       return;
05911    }
05912 
05913    /* yay */
05914    sla_load_config(1);
05915    sla.reload = 0;
05916 
05917    ast_mutex_unlock(&sla.lock);
05918 }
05919 
05920 static void *sla_thread(void *data)
05921 {
05922    struct sla_failed_station *failed_station;
05923    struct sla_ringing_station *ringing_station;
05924 
05925    ast_mutex_lock(&sla.lock);
05926 
05927    while (!sla.stop) {
05928       struct sla_event *event;
05929       struct timespec ts = { 0, };
05930       unsigned int have_timeout = 0;
05931 
05932       if (AST_LIST_EMPTY(&sla.event_q)) {
05933          if ((have_timeout = sla_process_timers(&ts)))
05934             ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
05935          else
05936             ast_cond_wait(&sla.cond, &sla.lock);
05937          if (sla.stop)
05938             break;
05939       }
05940 
05941       if (have_timeout)
05942          sla_process_timers(NULL);
05943 
05944       while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
05945          ast_mutex_unlock(&sla.lock);
05946          switch (event->type) {
05947          case SLA_EVENT_HOLD:
05948             sla_handle_hold_event(event);
05949             break;
05950          case SLA_EVENT_DIAL_STATE:
05951             sla_handle_dial_state_event();
05952             break;
05953          case SLA_EVENT_RINGING_TRUNK:
05954             sla_handle_ringing_trunk_event();
05955             break;
05956          case SLA_EVENT_RELOAD:
05957             sla.reload = 1;
05958          case SLA_EVENT_CHECK_RELOAD:
05959             break;
05960          }
05961          ast_free(event);
05962          ast_mutex_lock(&sla.lock);
05963       }
05964 
05965       if (sla.reload) {
05966          sla_check_reload();
05967       }
05968    }
05969 
05970    ast_mutex_unlock(&sla.lock);
05971 
05972    while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry)))
05973       ast_free(ringing_station);
05974 
05975    while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry)))
05976       ast_free(failed_station);
05977 
05978    return NULL;
05979 }
05980 
05981 struct dial_trunk_args {
05982    struct sla_trunk_ref *trunk_ref;
05983    struct sla_station *station;
05984    ast_mutex_t *cond_lock;
05985    ast_cond_t *cond;
05986 };
05987 
05988 static void *dial_trunk(void *data)
05989 {
05990    struct dial_trunk_args *args = data;
05991    struct ast_dial *dial;
05992    char *tech, *tech_data;
05993    enum ast_dial_result dial_res;
05994    char conf_name[MAX_CONFNUM];
05995    struct ast_conference *conf;
05996    struct ast_flags64 conf_flags = { 0 };
05997    struct sla_trunk_ref *trunk_ref = args->trunk_ref;
05998    int caller_is_saved;
05999    struct ast_party_caller caller;
06000 
06001    if (!(dial = ast_dial_create())) {
06002       ast_mutex_lock(args->cond_lock);
06003       ast_cond_signal(args->cond);
06004       ast_mutex_unlock(args->cond_lock);
06005       return NULL;
06006    }
06007 
06008    tech_data = ast_strdupa(trunk_ref->trunk->device);
06009    tech = strsep(&tech_data, "/");
06010    if (ast_dial_append(dial, tech, tech_data) == -1) {
06011       ast_mutex_lock(args->cond_lock);
06012       ast_cond_signal(args->cond);
06013       ast_mutex_unlock(args->cond_lock);
06014       ast_dial_destroy(dial);
06015       return NULL;
06016    }
06017 
06018    /* Do we need to save of the caller ID data? */
06019    caller_is_saved = 0;
06020    if (!sla.attempt_callerid) {
06021       caller_is_saved = 1;
06022       caller = trunk_ref->chan->caller;
06023       ast_party_caller_init(&trunk_ref->chan->caller);
06024    }
06025 
06026    dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
06027 
06028    /* Restore saved caller ID */
06029    if (caller_is_saved) {
06030       ast_party_caller_free(&trunk_ref->chan->caller);
06031       trunk_ref->chan->caller = caller;
06032    }
06033 
06034    if (dial_res != AST_DIAL_RESULT_TRYING) {
06035       ast_mutex_lock(args->cond_lock);
06036       ast_cond_signal(args->cond);
06037       ast_mutex_unlock(args->cond_lock);
06038       ast_dial_destroy(dial);
06039       return NULL;
06040    }
06041 
06042    for (;;) {
06043       unsigned int done = 0;
06044       switch ((dial_res = ast_dial_state(dial))) {
06045       case AST_DIAL_RESULT_ANSWERED:
06046          trunk_ref->trunk->chan = ast_dial_answered(dial);
06047       case AST_DIAL_RESULT_HANGUP:
06048       case AST_DIAL_RESULT_INVALID:
06049       case AST_DIAL_RESULT_FAILED:
06050       case AST_DIAL_RESULT_TIMEOUT:
06051       case AST_DIAL_RESULT_UNANSWERED:
06052          done = 1;
06053       case AST_DIAL_RESULT_TRYING:
06054       case AST_DIAL_RESULT_RINGING:
06055       case AST_DIAL_RESULT_PROGRESS:
06056       case AST_DIAL_RESULT_PROCEEDING:
06057          break;
06058       }
06059       if (done)
06060          break;
06061    }
06062 
06063    if (!trunk_ref->trunk->chan) {
06064       ast_mutex_lock(args->cond_lock);
06065       ast_cond_signal(args->cond);
06066       ast_mutex_unlock(args->cond_lock);
06067       ast_dial_join(dial);
06068       ast_dial_destroy(dial);
06069       return NULL;
06070    }
06071 
06072    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06073    ast_set_flag64(&conf_flags, 
06074       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | 
06075       CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
06076    conf = build_conf(conf_name, "", "", 1, 1, 1, trunk_ref->trunk->chan, NULL);
06077 
06078    ast_mutex_lock(args->cond_lock);
06079    ast_cond_signal(args->cond);
06080    ast_mutex_unlock(args->cond_lock);
06081 
06082    if (conf) {
06083       conf_run(trunk_ref->trunk->chan, conf, &conf_flags, NULL);
06084       dispose_conf(conf);
06085       conf = NULL;
06086    }
06087 
06088    /* If the trunk is going away, it is definitely now IDLE. */
06089    sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06090 
06091    trunk_ref->trunk->chan = NULL;
06092    trunk_ref->trunk->on_hold = 0;
06093 
06094    ast_dial_join(dial);
06095    ast_dial_destroy(dial);
06096 
06097    return NULL;
06098 }
06099 
06100 /*! \brief For a given station, choose the highest priority idle trunk
06101  */
06102 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
06103 {
06104    struct sla_trunk_ref *trunk_ref = NULL;
06105 
06106    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06107       if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
06108          break;
06109    }
06110 
06111    return trunk_ref;
06112 }
06113 
06114 static int sla_station_exec(struct ast_channel *chan, const char *data)
06115 {
06116    char *station_name, *trunk_name;
06117    struct sla_station *station;
06118    struct sla_trunk_ref *trunk_ref = NULL;
06119    char conf_name[MAX_CONFNUM];
06120    struct ast_flags64 conf_flags = { 0 };
06121    struct ast_conference *conf;
06122 
06123    if (ast_strlen_zero(data)) {
06124       ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06125       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06126       return 0;
06127    }
06128 
06129    trunk_name = ast_strdupa(data);
06130    station_name = strsep(&trunk_name, "_");
06131 
06132    if (ast_strlen_zero(station_name)) {
06133       ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06134       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06135       return 0;
06136    }
06137 
06138    AST_RWLIST_RDLOCK(&sla_stations);
06139    station = sla_find_station(station_name);
06140    if (station)
06141       ast_atomic_fetchadd_int((int *) &station->ref_count, 1);
06142    AST_RWLIST_UNLOCK(&sla_stations);
06143 
06144    if (!station) {
06145       ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
06146       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06147       sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06148       return 0;
06149    }
06150 
06151    AST_RWLIST_RDLOCK(&sla_trunks);
06152    if (!ast_strlen_zero(trunk_name)) {
06153       trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
06154    } else
06155       trunk_ref = sla_choose_idle_trunk(station);
06156    AST_RWLIST_UNLOCK(&sla_trunks);
06157 
06158    if (!trunk_ref) {
06159       if (ast_strlen_zero(trunk_name))
06160          ast_log(LOG_NOTICE, "No trunks available for call.\n");
06161       else {
06162          ast_log(LOG_NOTICE, "Can't join existing call on trunk "
06163             "'%s' due to access controls.\n", trunk_name);
06164       }
06165       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06166       ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
06167       sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06168       return 0;
06169    }
06170 
06171    if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
06172       if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
06173          sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06174       else {
06175          trunk_ref->state = SLA_TRUNK_STATE_UP;
06176          ast_devstate_changed(AST_DEVICE_INUSE, 
06177             "SLA:%s_%s", station->name, trunk_ref->trunk->name);
06178       }
06179    } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
06180       struct sla_ringing_trunk *ringing_trunk;
06181 
06182       ast_mutex_lock(&sla.lock);
06183       AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06184          if (ringing_trunk->trunk == trunk_ref->trunk) {
06185             AST_LIST_REMOVE_CURRENT(entry);
06186             break;
06187          }
06188       }
06189       AST_LIST_TRAVERSE_SAFE_END
06190       ast_mutex_unlock(&sla.lock);
06191 
06192       if (ringing_trunk) {
06193          answer_trunk_chan(ringing_trunk->trunk->chan);
06194          sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06195 
06196          free(ringing_trunk);
06197 
06198          /* Queue up reprocessing ringing trunks, and then ringing stations again */
06199          sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06200          sla_queue_event(SLA_EVENT_DIAL_STATE);
06201       }
06202    }
06203 
06204    trunk_ref->chan = chan;
06205 
06206    if (!trunk_ref->trunk->chan) {
06207       ast_mutex_t cond_lock;
06208       ast_cond_t cond;
06209       pthread_t dont_care;
06210       struct dial_trunk_args args = {
06211          .trunk_ref = trunk_ref,
06212          .station = station,
06213          .cond_lock = &cond_lock,
06214          .cond = &cond,
06215       };
06216       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06217       /* Create a thread to dial the trunk and dump it into the conference.
06218        * However, we want to wait until the trunk has been dialed and the
06219        * conference is created before continuing on here. */
06220       ast_autoservice_start(chan);
06221       ast_mutex_init(&cond_lock);
06222       ast_cond_init(&cond, NULL);
06223       ast_mutex_lock(&cond_lock);
06224       ast_pthread_create_detached_background(&dont_care, NULL, dial_trunk, &args);
06225       ast_cond_wait(&cond, &cond_lock);
06226       ast_mutex_unlock(&cond_lock);
06227       ast_mutex_destroy(&cond_lock);
06228       ast_cond_destroy(&cond);
06229       ast_autoservice_stop(chan);
06230       if (!trunk_ref->trunk->chan) {
06231          ast_debug(1, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
06232          pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06233          sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06234          trunk_ref->chan = NULL;
06235          ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
06236          sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06237          return 0;
06238       }
06239    }
06240 
06241    if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
06242       trunk_ref->trunk->on_hold) {
06243       trunk_ref->trunk->on_hold = 0;
06244       ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
06245       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06246    }
06247 
06248    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06249    ast_set_flag64(&conf_flags, 
06250       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
06251    ast_answer(chan);
06252    conf = build_conf(conf_name, "", "", 0, 0, 1, chan, NULL);
06253    if (conf) {
06254       conf_run(chan, conf, &conf_flags, NULL);
06255       dispose_conf(conf);
06256       conf = NULL;
06257    }
06258    trunk_ref->chan = NULL;
06259    if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
06260       trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
06261       strncat(conf_name, ",K", sizeof(conf_name) - strlen(conf_name) - 1);
06262       admin_exec(NULL, conf_name);
06263       trunk_ref->trunk->hold_stations = 0;
06264       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06265    }
06266    
06267    pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
06268 
06269    ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
06270    sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06271 
06272    return 0;
06273 }
06274 
06275 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
06276 {
06277    struct sla_trunk_ref *trunk_ref;
06278 
06279    if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
06280       return NULL;
06281 
06282    trunk_ref->trunk = trunk;
06283 
06284    return trunk_ref;
06285 }
06286 
06287 static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
06288 {
06289    struct sla_ringing_trunk *ringing_trunk;
06290 
06291    if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
06292       return NULL;
06293    
06294    ringing_trunk->trunk = trunk;
06295    ringing_trunk->ring_begin = ast_tvnow();
06296 
06297    sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
06298 
06299    ast_mutex_lock(&sla.lock);
06300    AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
06301    ast_mutex_unlock(&sla.lock);
06302 
06303    sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06304 
06305    return ringing_trunk;
06306 }
06307 
06308 enum {
06309    SLA_TRUNK_OPT_MOH = (1 << 0),
06310 };
06311 
06312 enum {
06313    SLA_TRUNK_OPT_ARG_MOH_CLASS = 0,
06314    SLA_TRUNK_OPT_ARG_ARRAY_SIZE = 1,
06315 };
06316 
06317 AST_APP_OPTIONS(sla_trunk_opts, BEGIN_OPTIONS
06318    AST_APP_OPTION_ARG('M', SLA_TRUNK_OPT_MOH, SLA_TRUNK_OPT_ARG_MOH_CLASS),
06319 END_OPTIONS );
06320 
06321 static int sla_trunk_exec(struct ast_channel *chan, const char *data)
06322 {
06323    char conf_name[MAX_CONFNUM];
06324    struct ast_conference *conf;
06325    struct ast_flags64 conf_flags = { 0 };
06326    struct sla_trunk *trunk;
06327    struct sla_ringing_trunk *ringing_trunk;
06328    AST_DECLARE_APP_ARGS(args,
06329       AST_APP_ARG(trunk_name);
06330       AST_APP_ARG(options);
06331    );
06332    char *opts[SLA_TRUNK_OPT_ARG_ARRAY_SIZE] = { NULL, };
06333    struct ast_flags opt_flags = { 0 };
06334    char *parse;
06335 
06336    if (ast_strlen_zero(data)) {
06337       ast_log(LOG_ERROR, "The SLATrunk application requires an argument, the trunk name\n");
06338       return -1;
06339    }
06340 
06341    parse = ast_strdupa(data);
06342    AST_STANDARD_APP_ARGS(args, parse);
06343    if (args.argc == 2) {
06344       if (ast_app_parse_options(sla_trunk_opts, &opt_flags, opts, args.options)) {
06345          ast_log(LOG_ERROR, "Error parsing options for SLATrunk\n");
06346          return -1;
06347       }
06348    }
06349 
06350    AST_RWLIST_RDLOCK(&sla_trunks);
06351    trunk = sla_find_trunk(args.trunk_name);
06352    if (trunk)
06353       ast_atomic_fetchadd_int((int *) &trunk->ref_count, 1);
06354    AST_RWLIST_UNLOCK(&sla_trunks);
06355 
06356    if (!trunk) {
06357       ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", args.trunk_name);
06358       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06359       sla_queue_event(SLA_EVENT_CHECK_RELOAD);  
06360       return 0;
06361    }
06362 
06363    if (trunk->chan) {
06364       ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
06365          args.trunk_name);
06366       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06367       ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
06368       sla_queue_event(SLA_EVENT_CHECK_RELOAD);  
06369       return 0;
06370    }
06371 
06372    trunk->chan = chan;
06373 
06374    if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
06375       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06376       ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
06377       sla_queue_event(SLA_EVENT_CHECK_RELOAD);  
06378       return 0;
06379    }
06380 
06381    snprintf(conf_name, sizeof(conf_name), "SLA_%s", args.trunk_name);
06382    conf = build_conf(conf_name, "", "", 1, 1, 1, chan, NULL);
06383    if (!conf) {
06384       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06385       ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
06386       sla_queue_event(SLA_EVENT_CHECK_RELOAD);  
06387       return 0;
06388    }
06389    ast_set_flag64(&conf_flags, 
06390       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF | CONFFLAG_NO_AUDIO_UNTIL_UP);
06391 
06392    if (ast_test_flag(&opt_flags, SLA_TRUNK_OPT_MOH)) {
06393       ast_indicate(chan, -1);
06394       ast_set_flag64(&conf_flags, CONFFLAG_MOH);
06395    } else
06396       ast_indicate(chan, AST_CONTROL_RINGING);
06397 
06398    conf_run(chan, conf, &conf_flags, opts);
06399    dispose_conf(conf);
06400    conf = NULL;
06401    trunk->chan = NULL;
06402    trunk->on_hold = 0;
06403 
06404    sla_change_trunk_state(trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06405 
06406    if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
06407       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
06408 
06409    /* Remove the entry from the list of ringing trunks if it is still there. */
06410    ast_mutex_lock(&sla.lock);
06411    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06412       if (ringing_trunk->trunk == trunk) {
06413          AST_LIST_REMOVE_CURRENT(entry);
06414          break;
06415       }
06416    }
06417    AST_LIST_TRAVERSE_SAFE_END;
06418    ast_mutex_unlock(&sla.lock);
06419    if (ringing_trunk) {
06420       ast_free(ringing_trunk);
06421       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
06422       /* Queue reprocessing of ringing trunks to make stations stop ringing
06423        * that shouldn't be ringing after this trunk stopped. */
06424       sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06425    }
06426 
06427    ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
06428    sla_queue_event(SLA_EVENT_CHECK_RELOAD);  
06429 
06430    return 0;
06431 }
06432 
06433 static enum ast_device_state sla_state(const char *data)
06434 {
06435    char *buf, *station_name, *trunk_name;
06436    struct sla_station *station;
06437    struct sla_trunk_ref *trunk_ref;
06438    enum ast_device_state res = AST_DEVICE_INVALID;
06439 
06440    trunk_name = buf = ast_strdupa(data);
06441    station_name = strsep(&trunk_name, "_");
06442 
06443    AST_RWLIST_RDLOCK(&sla_stations);
06444    AST_LIST_TRAVERSE(&sla_stations, station, entry) {
06445       if (strcasecmp(station_name, station->name))
06446          continue;
06447       AST_RWLIST_RDLOCK(&sla_trunks);
06448       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06449          if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
06450             break;
06451       }
06452       if (!trunk_ref) {
06453          AST_RWLIST_UNLOCK(&sla_trunks);
06454          break;
06455       }
06456       res = sla_state_to_devstate(trunk_ref->state);
06457       AST_RWLIST_UNLOCK(&sla_trunks);
06458    }
06459    AST_RWLIST_UNLOCK(&sla_stations);
06460 
06461    if (res == AST_DEVICE_INVALID) {
06462       ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
06463          trunk_name, station_name);
06464    }
06465 
06466    return res;
06467 }
06468 
06469 static void destroy_trunk(struct sla_trunk *trunk)
06470 {
06471    struct sla_station_ref *station_ref;
06472 
06473    if (!ast_strlen_zero(trunk->autocontext))
06474       ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
06475 
06476    while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
06477       ast_free(station_ref);
06478 
06479    ast_string_field_free_memory(trunk);
06480    ast_free(trunk);
06481 }
06482 
06483 static void destroy_station(struct sla_station *station)
06484 {
06485    struct sla_trunk_ref *trunk_ref;
06486 
06487    if (!ast_strlen_zero(station->autocontext)) {
06488       AST_RWLIST_RDLOCK(&sla_trunks);
06489       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06490          char exten[AST_MAX_EXTENSION];
06491          char hint[AST_MAX_APP];
06492          snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
06493          snprintf(hint, sizeof(hint), "SLA:%s", exten);
06494          ast_context_remove_extension(station->autocontext, exten, 
06495             1, sla_registrar);
06496          ast_context_remove_extension(station->autocontext, hint, 
06497             PRIORITY_HINT, sla_registrar);
06498       }
06499       AST_RWLIST_UNLOCK(&sla_trunks);
06500    }
06501 
06502    while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
06503       ast_free(trunk_ref);
06504 
06505    ast_string_field_free_memory(station);
06506    ast_free(station);
06507 }
06508 
06509 static void sla_destroy(void)
06510 {
06511    struct sla_trunk *trunk;
06512    struct sla_station *station;
06513 
06514    AST_RWLIST_WRLOCK(&sla_trunks);
06515    while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
06516       destroy_trunk(trunk);
06517    AST_RWLIST_UNLOCK(&sla_trunks);
06518 
06519    AST_RWLIST_WRLOCK(&sla_stations);
06520    while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
06521       destroy_station(station);
06522    AST_RWLIST_UNLOCK(&sla_stations);
06523 
06524    if (sla.thread != AST_PTHREADT_NULL) {
06525       ast_mutex_lock(&sla.lock);
06526       sla.stop = 1;
06527       ast_cond_signal(&sla.cond);
06528       ast_mutex_unlock(&sla.lock);
06529       pthread_join(sla.thread, NULL);
06530    }
06531 
06532    /* Drop any created contexts from the dialplan */
06533    ast_context_destroy(NULL, sla_registrar);
06534 
06535    ast_mutex_destroy(&sla.lock);
06536    ast_cond_destroy(&sla.cond);
06537 }
06538 
06539 static int sla_check_device(const char *device)
06540 {
06541    char *tech, *tech_data;
06542 
06543    tech_data = ast_strdupa(device);
06544    tech = strsep(&tech_data, "/");
06545 
06546    if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
06547       return -1;
06548 
06549    return 0;
06550 }
06551 
06552 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
06553 {
06554    struct sla_trunk *trunk;
06555    struct ast_variable *var;
06556    const char *dev;
06557 
06558    if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
06559       ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
06560       return -1;
06561    }
06562 
06563    if (sla_check_device(dev)) {
06564       ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
06565          cat, dev);
06566       return -1;
06567    }
06568 
06569    if (!(trunk = ast_calloc_with_stringfields(1, struct sla_trunk, 32))) {
06570       return -1;
06571    }
06572 
06573    ast_string_field_set(trunk, name, cat);
06574    ast_string_field_set(trunk, device, dev);
06575 
06576    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
06577       if (!strcasecmp(var->name, "autocontext"))
06578          ast_string_field_set(trunk, autocontext, var->value);
06579       else if (!strcasecmp(var->name, "ringtimeout")) {
06580          if (sscanf(var->value, "%30u", &trunk->ring_timeout) != 1) {
06581             ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
06582                var->value, trunk->name);
06583             trunk->ring_timeout = 0;
06584          }
06585       } else if (!strcasecmp(var->name, "barge"))
06586          trunk->barge_disabled = ast_false(var->value);
06587       else if (!strcasecmp(var->name, "hold")) {
06588          if (!strcasecmp(var->value, "private"))
06589             trunk->hold_access = SLA_HOLD_PRIVATE;
06590          else if (!strcasecmp(var->value, "open"))
06591             trunk->hold_access = SLA_HOLD_OPEN;
06592          else {
06593             ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
06594                var->value, trunk->name);
06595          }
06596       } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
06597          ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
06598             var->name, var->lineno, SLA_CONFIG_FILE);
06599       }
06600    }
06601 
06602    if (!ast_strlen_zero(trunk->autocontext)) {
06603       struct ast_context *context;
06604       context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
06605       if (!context) {
06606          ast_log(LOG_ERROR, "Failed to automatically find or create "
06607             "context '%s' for SLA!\n", trunk->autocontext);
06608          destroy_trunk(trunk);
06609          return -1;
06610       }
06611       if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
06612          NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
06613          ast_log(LOG_ERROR, "Failed to automatically create extension "
06614             "for trunk '%s'!\n", trunk->name);
06615          destroy_trunk(trunk);
06616          return -1;
06617       }
06618    }
06619 
06620    AST_RWLIST_WRLOCK(&sla_trunks);
06621    AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
06622    AST_RWLIST_UNLOCK(&sla_trunks);
06623 
06624    return 0;
06625 }
06626 
06627 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
06628 {
06629    struct sla_trunk *trunk;
06630    struct sla_trunk_ref *trunk_ref;
06631    struct sla_station_ref *station_ref;
06632    char *trunk_name, *options, *cur;
06633 
06634    options = ast_strdupa(var->value);
06635    trunk_name = strsep(&options, ",");
06636    
06637    AST_RWLIST_RDLOCK(&sla_trunks);
06638    AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
06639       if (!strcasecmp(trunk->name, trunk_name))
06640          break;
06641    }
06642 
06643    AST_RWLIST_UNLOCK(&sla_trunks);
06644    if (!trunk) {
06645       ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
06646       return;
06647    }
06648    if (!(trunk_ref = create_trunk_ref(trunk)))
06649       return;
06650    trunk_ref->state = SLA_TRUNK_STATE_IDLE;
06651 
06652    while ((cur = strsep(&options, ","))) {
06653       char *name, *value = cur;
06654       name = strsep(&value, "=");
06655       if (!strcasecmp(name, "ringtimeout")) {
06656          if (sscanf(value, "%30u", &trunk_ref->ring_timeout) != 1) {
06657             ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
06658                "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
06659             trunk_ref->ring_timeout = 0;
06660          }
06661       } else if (!strcasecmp(name, "ringdelay")) {
06662          if (sscanf(value, "%30u", &trunk_ref->ring_delay) != 1) {
06663             ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
06664                "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
06665             trunk_ref->ring_delay = 0;
06666          }
06667       } else {
06668          ast_log(LOG_WARNING, "Invalid option '%s' for "
06669             "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
06670       }
06671    }
06672 
06673    if (!(station_ref = sla_create_station_ref(station))) {
06674       ast_free(trunk_ref);
06675       return;
06676    }
06677    ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
06678    AST_RWLIST_WRLOCK(&sla_trunks);
06679    AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
06680    AST_RWLIST_UNLOCK(&sla_trunks);
06681    AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
06682 }
06683 
06684 static int sla_build_station(struct ast_config *cfg, const char *cat)
06685 {
06686    struct sla_station *station;
06687    struct ast_variable *var;
06688    const char *dev;
06689 
06690    if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
06691       ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
06692       return -1;
06693    }
06694 
06695    if (!(station = ast_calloc_with_stringfields(1, struct sla_station, 32))) {
06696       return -1;
06697    }
06698 
06699    ast_string_field_set(station, name, cat);
06700    ast_string_field_set(station, device, dev);
06701 
06702    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
06703       if (!strcasecmp(var->name, "trunk"))
06704          sla_add_trunk_to_station(station, var);
06705       else if (!strcasecmp(var->name, "autocontext"))
06706          ast_string_field_set(station, autocontext, var->value);
06707       else if (!strcasecmp(var->name, "ringtimeout")) {
06708          if (sscanf(var->value, "%30u", &station->ring_timeout) != 1) {
06709             ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
06710                var->value, station->name);
06711             station->ring_timeout = 0;
06712          }
06713       } else if (!strcasecmp(var->name, "ringdelay")) {
06714          if (sscanf(var->value, "%30u", &station->ring_delay) != 1) {
06715             ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
06716                var->value, station->name);
06717             station->ring_delay = 0;
06718          }
06719       } else if (!strcasecmp(var->name, "hold")) {
06720          if (!strcasecmp(var->value, "private"))
06721             station->hold_access = SLA_HOLD_PRIVATE;
06722          else if (!strcasecmp(var->value, "open"))
06723             station->hold_access = SLA_HOLD_OPEN;
06724          else {
06725             ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
06726                var->value, station->name);
06727          }
06728 
06729       } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
06730          ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
06731             var->name, var->lineno, SLA_CONFIG_FILE);
06732       }
06733    }
06734 
06735    if (!ast_strlen_zero(station->autocontext)) {
06736       struct ast_context *context;
06737       struct sla_trunk_ref *trunk_ref;
06738       context = ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar);
06739       if (!context) {
06740          ast_log(LOG_ERROR, "Failed to automatically find or create "
06741             "context '%s' for SLA!\n", station->autocontext);
06742          destroy_station(station);
06743          return -1;
06744       }
06745       /* The extension for when the handset goes off-hook.
06746        * exten => station1,1,SLAStation(station1) */
06747       if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
06748          NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
06749          ast_log(LOG_ERROR, "Failed to automatically create extension "
06750             "for trunk '%s'!\n", station->name);
06751          destroy_station(station);
06752          return -1;
06753       }
06754       AST_RWLIST_RDLOCK(&sla_trunks);
06755       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06756          char exten[AST_MAX_EXTENSION];
06757          char hint[AST_MAX_APP];
06758          snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
06759          snprintf(hint, sizeof(hint), "SLA:%s", exten);
06760          /* Extension for this line button 
06761           * exten => station1_line1,1,SLAStation(station1_line1) */
06762          if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
06763             NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
06764             ast_log(LOG_ERROR, "Failed to automatically create extension "
06765                "for trunk '%s'!\n", station->name);
06766             destroy_station(station);
06767             return -1;
06768          }
06769          /* Hint for this line button 
06770           * exten => station1_line1,hint,SLA:station1_line1 */
06771          if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
06772             NULL, NULL, hint, NULL, NULL, sla_registrar)) {
06773             ast_log(LOG_ERROR, "Failed to automatically create hint "
06774                "for trunk '%s'!\n", station->name);
06775             destroy_station(station);
06776             return -1;
06777          }
06778       }
06779       AST_RWLIST_UNLOCK(&sla_trunks);
06780    }
06781 
06782    AST_RWLIST_WRLOCK(&sla_stations);
06783    AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
06784    AST_RWLIST_UNLOCK(&sla_stations);
06785 
06786    return 0;
06787 }
06788 
06789 static int sla_load_config(int reload)
06790 {
06791    struct ast_config *cfg;
06792    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06793    const char *cat = NULL;
06794    int res = 0;
06795    const char *val;
06796 
06797    if (!reload) {
06798       ast_mutex_init(&sla.lock);
06799       ast_cond_init(&sla.cond, NULL);
06800    }
06801 
06802    if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags))) {
06803       return 0; /* Treat no config as normal */
06804    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06805       return 0;
06806    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06807       ast_log(LOG_ERROR, "Config file " SLA_CONFIG_FILE " is in an invalid format.  Aborting.\n");
06808       return 0;
06809    }
06810 
06811    if (reload) {
06812       struct sla_station *station;
06813       struct sla_trunk *trunk;
06814 
06815       /* We need to actually delete the previous versions of trunks and stations now */
06816       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&sla_stations, station, entry) {
06817          AST_RWLIST_REMOVE_CURRENT(entry);
06818          ast_free(station);
06819       }
06820       AST_RWLIST_TRAVERSE_SAFE_END;
06821 
06822       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&sla_trunks, trunk, entry) {
06823          AST_RWLIST_REMOVE_CURRENT(entry);
06824          ast_free(trunk);
06825       }
06826       AST_RWLIST_TRAVERSE_SAFE_END;
06827    }
06828 
06829    if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
06830       sla.attempt_callerid = ast_true(val);
06831 
06832    while ((cat = ast_category_browse(cfg, cat)) && !res) {
06833       const char *type;
06834       if (!strcasecmp(cat, "general"))
06835          continue;
06836       if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
06837          ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
06838             SLA_CONFIG_FILE);
06839          continue;
06840       }
06841       if (!strcasecmp(type, "trunk"))
06842          res = sla_build_trunk(cfg, cat);
06843       else if (!strcasecmp(type, "station"))
06844          res = sla_build_station(cfg, cat);
06845       else {
06846          ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
06847             SLA_CONFIG_FILE, type);
06848       }
06849    }
06850 
06851    ast_config_destroy(cfg);
06852 
06853    /* Even if we don't have any stations, we may after a reload and we need to
06854     * be able to process the SLA_EVENT_RELOAD event in that case */
06855    if (sla.thread == AST_PTHREADT_NULL && (!AST_LIST_EMPTY(&sla_stations) || !AST_LIST_EMPTY(&sla_trunks))) {
06856       ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
06857    }
06858 
06859    return res;
06860 }
06861 
06862 static int acf_meetme_info_eval(const char *keyword, const struct ast_conference *conf)
06863 {
06864    if (!strcasecmp("lock", keyword)) {
06865       return conf->locked;
06866    } else if (!strcasecmp("parties", keyword)) {
06867       return conf->users;
06868    } else if (!strcasecmp("activity", keyword)) {
06869       time_t now;
06870       now = time(NULL);
06871       return (now - conf->start);
06872    } else if (!strcasecmp("dynamic", keyword)) {
06873       return conf->isdynamic;
06874    } else {
06875       return -1;
06876    }
06877 
06878 }
06879 
06880 static int acf_meetme_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06881 {
06882    struct ast_conference *conf;
06883    char *parse;
06884    int result = -2; /* only non-negative numbers valid, -1 is used elsewhere */
06885    AST_DECLARE_APP_ARGS(args,
06886       AST_APP_ARG(keyword);
06887       AST_APP_ARG(confno);
06888    );
06889 
06890    if (ast_strlen_zero(data)) {
06891       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires two arguments\n");
06892       return -1;
06893    }
06894 
06895    parse = ast_strdupa(data);
06896    AST_STANDARD_APP_ARGS(args, parse);
06897 
06898    if (ast_strlen_zero(args.keyword)) {
06899       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a keyword\n");
06900       return -1;
06901    }
06902 
06903    if (ast_strlen_zero(args.confno)) {
06904       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a conference number\n");
06905       return -1;
06906    }
06907 
06908    AST_LIST_LOCK(&confs);
06909    AST_LIST_TRAVERSE(&confs, conf, list) {
06910       if (!strcmp(args.confno, conf->confno)) {
06911          result = acf_meetme_info_eval(args.keyword, conf);
06912          break;
06913       }
06914    }
06915    AST_LIST_UNLOCK(&confs);
06916 
06917    if (result > -1) {
06918       snprintf(buf, len, "%d", result);
06919    } else if (result == -1) {
06920       ast_log(LOG_NOTICE, "Error: invalid keyword: '%s'\n", args.keyword);
06921       snprintf(buf, len, "0");
06922    } else if (result == -2) {
06923       ast_log(LOG_NOTICE, "Error: conference (%s) not found\n", args.confno); 
06924       snprintf(buf, len, "0");
06925    }
06926 
06927    return 0;
06928 }
06929 
06930 
06931 static struct ast_custom_function meetme_info_acf = {
06932    .name = "MEETME_INFO",
06933    .read = acf_meetme_info,
06934 };
06935 
06936 
06937 static int load_config(int reload)
06938 {
06939    load_config_meetme();
06940 
06941    if (reload && sla.thread != AST_PTHREADT_NULL) {
06942       sla_queue_event(SLA_EVENT_RELOAD);
06943       ast_log(LOG_NOTICE, "A reload of the SLA configuration has been requested "
06944          "and will be completed when the system is idle.\n");
06945       return 0;
06946    }
06947    
06948    return sla_load_config(0);
06949 }
06950 
06951 #define MEETME_DATA_EXPORT(MEMBER)              \
06952    MEMBER(ast_conference, confno, AST_DATA_STRING)       \
06953    MEMBER(ast_conference, dahdiconf, AST_DATA_INTEGER)      \
06954    MEMBER(ast_conference, users, AST_DATA_INTEGER)       \
06955    MEMBER(ast_conference, markedusers, AST_DATA_INTEGER)    \
06956    MEMBER(ast_conference, maxusers, AST_DATA_INTEGER)    \
06957    MEMBER(ast_conference, isdynamic, AST_DATA_BOOLEAN)      \
06958    MEMBER(ast_conference, locked, AST_DATA_BOOLEAN)      \
06959    MEMBER(ast_conference, recordingfilename, AST_DATA_STRING)  \
06960    MEMBER(ast_conference, recordingformat, AST_DATA_STRING) \
06961    MEMBER(ast_conference, pin, AST_DATA_PASSWORD)        \
06962    MEMBER(ast_conference, pinadmin, AST_DATA_PASSWORD)      \
06963    MEMBER(ast_conference, start, AST_DATA_TIMESTAMP)     \
06964    MEMBER(ast_conference, endtime, AST_DATA_TIMESTAMP)
06965 
06966 AST_DATA_STRUCTURE(ast_conference, MEETME_DATA_EXPORT);
06967 
06968 #define MEETME_USER_DATA_EXPORT(MEMBER)               \
06969    MEMBER(ast_conf_user, user_no, AST_DATA_INTEGER)      \
06970    MEMBER(ast_conf_user, talking, AST_DATA_BOOLEAN)      \
06971    MEMBER(ast_conf_user, dahdichannel, AST_DATA_BOOLEAN)    \
06972    MEMBER(ast_conf_user, jointime, AST_DATA_TIMESTAMP)      \
06973    MEMBER(ast_conf_user, kicktime, AST_DATA_TIMESTAMP)      \
06974    MEMBER(ast_conf_user, timelimit, AST_DATA_MILLISECONDS)     \
06975    MEMBER(ast_conf_user, play_warning, AST_DATA_MILLISECONDS)  \
06976    MEMBER(ast_conf_user, warning_freq, AST_DATA_MILLISECONDS)
06977 
06978 AST_DATA_STRUCTURE(ast_conf_user, MEETME_USER_DATA_EXPORT);
06979 
06980 static int user_add_provider_cb(void *obj, void *arg, int flags)
06981 {
06982    struct ast_data *data_meetme_user;
06983    struct ast_data *data_meetme_user_channel;
06984    struct ast_data *data_meetme_user_volume;
06985 
06986    struct ast_conf_user *user = obj;
06987    struct ast_data *data_meetme_users = arg;
06988 
06989    data_meetme_user = ast_data_add_node(data_meetme_users, "user");
06990    if (!data_meetme_user) {
06991       return 0;
06992    }
06993    /* user structure */
06994    ast_data_add_structure(ast_conf_user, data_meetme_user, user);
06995 
06996    /* user's channel */
06997    data_meetme_user_channel = ast_data_add_node(data_meetme_user, "channel");
06998    if (!data_meetme_user_channel) {
06999       return 0;
07000    }
07001 
07002    ast_channel_data_add_structure(data_meetme_user_channel, user->chan, 1);
07003 
07004    /* volume structure */
07005    data_meetme_user_volume = ast_data_add_node(data_meetme_user, "listen-volume");
07006    if (!data_meetme_user_volume) {
07007       return 0;
07008    }
07009    ast_data_add_int(data_meetme_user_volume, "desired", user->listen.desired);
07010    ast_data_add_int(data_meetme_user_volume, "actual", user->listen.actual);
07011 
07012    data_meetme_user_volume = ast_data_add_node(data_meetme_user, "talk-volume");
07013    if (!data_meetme_user_volume) {
07014       return 0;
07015    }
07016    ast_data_add_int(data_meetme_user_volume, "desired", user->talk.desired);
07017    ast_data_add_int(data_meetme_user_volume, "actual", user->talk.actual);
07018 
07019    return 0;
07020 }
07021 
07022 /*!
07023  * \internal
07024  * \brief Implements the meetme data provider.
07025  */
07026 static int meetme_data_provider_get(const struct ast_data_search *search,
07027    struct ast_data *data_root)
07028 {
07029    struct ast_conference *cnf;
07030    struct ast_data *data_meetme, *data_meetme_users;
07031 
07032    AST_LIST_LOCK(&confs);
07033    AST_LIST_TRAVERSE(&confs, cnf, list) {
07034       data_meetme = ast_data_add_node(data_root, "meetme");
07035       if (!data_meetme) {
07036          continue;
07037       }
07038 
07039       ast_data_add_structure(ast_conference, data_meetme, cnf);
07040 
07041       if (ao2_container_count(cnf->usercontainer)) {
07042          data_meetme_users = ast_data_add_node(data_meetme, "users");
07043          if (!data_meetme_users) {
07044             ast_data_remove_node(data_root, data_meetme);
07045             continue;
07046          }
07047 
07048          ao2_callback(cnf->usercontainer, OBJ_NODATA, user_add_provider_cb, data_meetme_users); 
07049       }
07050 
07051       if (!ast_data_search_match(search, data_meetme)) {
07052          ast_data_remove_node(data_root, data_meetme);
07053       }
07054    }
07055    AST_LIST_UNLOCK(&confs);
07056 
07057    return 0;
07058 }
07059 
07060 static const struct ast_data_handler meetme_data_provider = {
07061    .version = AST_DATA_HANDLER_VERSION,
07062    .get = meetme_data_provider_get
07063 };
07064 
07065 static const struct ast_data_entry meetme_data_providers[] = {
07066    AST_DATA_ENTRY("asterisk/application/meetme/list", &meetme_data_provider),
07067 };
07068 
07069 #ifdef TEST_FRAMEWORK
07070 AST_TEST_DEFINE(test_meetme_data_provider)
07071 {
07072    struct ast_channel *chan;
07073    struct ast_conference *cnf;
07074    struct ast_data *node;
07075    struct ast_data_query query = {
07076       .path = "/asterisk/application/meetme/list",
07077       .search = "list/meetme/confno=9898"
07078    };
07079 
07080    switch (cmd) {
07081    case TEST_INIT:
07082       info->name = "meetme_get_data_test";
07083       info->category = "/main/data/app_meetme/list/";
07084       info->summary = "Meetme data provider unit test";
07085       info->description =
07086          "Tests whether the Meetme data provider implementation works as expected.";
07087       return AST_TEST_NOT_RUN;
07088    case TEST_EXECUTE:
07089       break;
07090    }
07091 
07092    chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, 0, 0, "MeetMeTest");
07093    if (!chan) {
07094       ast_test_status_update(test, "Channel allocation failed\n");
07095       return AST_TEST_FAIL;
07096    }
07097 
07098    cnf = build_conf("9898", "", "1234", 1, 1, 1, chan, test);
07099    if (!cnf) {
07100       ast_test_status_update(test, "Build of test conference 9898 failed\n");
07101       ast_hangup(chan);
07102       return AST_TEST_FAIL;
07103    }
07104 
07105    node = ast_data_get(&query);
07106    if (!node) {
07107       ast_test_status_update(test, "Data query for test conference 9898 failed\n");
07108       dispose_conf(cnf);
07109       ast_hangup(chan);
07110       return AST_TEST_FAIL;
07111    }
07112 
07113    if (strcmp(ast_data_retrieve_string(node, "meetme/confno"), "9898")) {
07114       ast_test_status_update(test, "Query returned the wrong conference\n");
07115       dispose_conf(cnf);
07116       ast_hangup(chan);
07117       ast_data_free(node);
07118       return AST_TEST_FAIL;
07119    }
07120 
07121    ast_data_free(node);
07122    dispose_conf(cnf);
07123    ast_hangup(chan);
07124 
07125    return AST_TEST_PASS;
07126 }
07127 #endif
07128 
07129 static int unload_module(void)
07130 {
07131    int res = 0;
07132    
07133    ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
07134    res = ast_manager_unregister("MeetmeMute");
07135    res |= ast_manager_unregister("MeetmeUnmute");
07136    res |= ast_manager_unregister("MeetmeList");
07137    res |= ast_unregister_application(app4);
07138    res |= ast_unregister_application(app3);
07139    res |= ast_unregister_application(app2);
07140    res |= ast_unregister_application(app);
07141    res |= ast_unregister_application(slastation_app);
07142    res |= ast_unregister_application(slatrunk_app);
07143 
07144 #ifdef TEST_FRAMEWORK
07145    AST_TEST_UNREGISTER(test_meetme_data_provider);
07146 #endif
07147    ast_data_unregister(NULL);
07148 
07149    ast_devstate_prov_del("Meetme");
07150    ast_devstate_prov_del("SLA");
07151    
07152    sla_destroy();
07153    
07154    res |= ast_custom_function_unregister(&meetme_info_acf);
07155    ast_unload_realtime("meetme");
07156 
07157    return res;
07158 }
07159 
07160 
07161 
07162 static int load_module(void)
07163 {
07164    int res = 0;
07165 
07166    res |= load_config(0);
07167 
07168    ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
07169    res |= ast_manager_register_xml("MeetmeMute", EVENT_FLAG_CALL, action_meetmemute);
07170    res |= ast_manager_register_xml("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute);
07171    res |= ast_manager_register_xml("MeetmeList", EVENT_FLAG_REPORTING, action_meetmelist);
07172    res |= ast_register_application_xml(app4, channel_admin_exec);
07173    res |= ast_register_application_xml(app3, admin_exec);
07174    res |= ast_register_application_xml(app2, count_exec);
07175    res |= ast_register_application_xml(app, conf_exec);
07176    res |= ast_register_application_xml(slastation_app, sla_station_exec);
07177    res |= ast_register_application_xml(slatrunk_app, sla_trunk_exec);
07178 
07179 #ifdef TEST_FRAMEWORK
07180    AST_TEST_REGISTER(test_meetme_data_provider);
07181 #endif
07182    ast_data_register_multiple(meetme_data_providers, ARRAY_LEN(meetme_data_providers));
07183 
07184    res |= ast_devstate_prov_add("Meetme", meetmestate);
07185    res |= ast_devstate_prov_add("SLA", sla_state);
07186 
07187    res |= ast_custom_function_register(&meetme_info_acf);
07188    ast_realtime_require_field("meetme", "confno", RQ_UINTEGER2, 3, "members", RQ_UINTEGER1, 3, NULL);
07189 
07190    return res;
07191 }
07192 
07193 static int reload(void)
07194 {
07195    ast_unload_realtime("meetme");
07196    return load_config(1);
07197 }
07198 
07199 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "MeetMe conference bridge",
07200       .load = load_module,
07201       .unload = unload_module,
07202       .reload = reload,
07203       .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
07204           );
07205 

Generated on Mon Mar 19 11:30:20 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7