Mon Mar 12 2012 21:18:27

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: 347369 $")
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    /* We need to actually delete the previous versions of trunks and stations now */
05914    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&sla_stations, station, entry) {
05915       AST_RWLIST_REMOVE_CURRENT(entry);
05916       ast_free(station);
05917    }
05918    AST_RWLIST_TRAVERSE_SAFE_END;
05919 
05920    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&sla_trunks, trunk, entry) {
05921       AST_RWLIST_REMOVE_CURRENT(entry);
05922       ast_free(trunk);
05923    }
05924    AST_RWLIST_TRAVERSE_SAFE_END;
05925 
05926    /* yay */
05927    sla_load_config(1);
05928    sla.reload = 0;
05929 
05930    ast_mutex_unlock(&sla.lock);
05931 }
05932 
05933 static void *sla_thread(void *data)
05934 {
05935    struct sla_failed_station *failed_station;
05936    struct sla_ringing_station *ringing_station;
05937 
05938    ast_mutex_lock(&sla.lock);
05939 
05940    while (!sla.stop) {
05941       struct sla_event *event;
05942       struct timespec ts = { 0, };
05943       unsigned int have_timeout = 0;
05944 
05945       if (AST_LIST_EMPTY(&sla.event_q)) {
05946          if ((have_timeout = sla_process_timers(&ts)))
05947             ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
05948          else
05949             ast_cond_wait(&sla.cond, &sla.lock);
05950          if (sla.stop)
05951             break;
05952       }
05953 
05954       if (have_timeout)
05955          sla_process_timers(NULL);
05956 
05957       while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
05958          ast_mutex_unlock(&sla.lock);
05959          switch (event->type) {
05960          case SLA_EVENT_HOLD:
05961             sla_handle_hold_event(event);
05962             break;
05963          case SLA_EVENT_DIAL_STATE:
05964             sla_handle_dial_state_event();
05965             break;
05966          case SLA_EVENT_RINGING_TRUNK:
05967             sla_handle_ringing_trunk_event();
05968             break;
05969          case SLA_EVENT_RELOAD:
05970             sla.reload = 1;
05971          case SLA_EVENT_CHECK_RELOAD:
05972             break;
05973          }
05974          ast_free(event);
05975          ast_mutex_lock(&sla.lock);
05976       }
05977 
05978       if (sla.reload) {
05979          sla_check_reload();
05980       }
05981    }
05982 
05983    ast_mutex_unlock(&sla.lock);
05984 
05985    while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry)))
05986       ast_free(ringing_station);
05987 
05988    while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry)))
05989       ast_free(failed_station);
05990 
05991    return NULL;
05992 }
05993 
05994 struct dial_trunk_args {
05995    struct sla_trunk_ref *trunk_ref;
05996    struct sla_station *station;
05997    ast_mutex_t *cond_lock;
05998    ast_cond_t *cond;
05999 };
06000 
06001 static void *dial_trunk(void *data)
06002 {
06003    struct dial_trunk_args *args = data;
06004    struct ast_dial *dial;
06005    char *tech, *tech_data;
06006    enum ast_dial_result dial_res;
06007    char conf_name[MAX_CONFNUM];
06008    struct ast_conference *conf;
06009    struct ast_flags64 conf_flags = { 0 };
06010    struct sla_trunk_ref *trunk_ref = args->trunk_ref;
06011    int caller_is_saved;
06012    struct ast_party_caller caller;
06013 
06014    if (!(dial = ast_dial_create())) {
06015       ast_mutex_lock(args->cond_lock);
06016       ast_cond_signal(args->cond);
06017       ast_mutex_unlock(args->cond_lock);
06018       return NULL;
06019    }
06020 
06021    tech_data = ast_strdupa(trunk_ref->trunk->device);
06022    tech = strsep(&tech_data, "/");
06023    if (ast_dial_append(dial, tech, tech_data) == -1) {
06024       ast_mutex_lock(args->cond_lock);
06025       ast_cond_signal(args->cond);
06026       ast_mutex_unlock(args->cond_lock);
06027       ast_dial_destroy(dial);
06028       return NULL;
06029    }
06030 
06031    /* Do we need to save of the caller ID data? */
06032    caller_is_saved = 0;
06033    if (!sla.attempt_callerid) {
06034       caller_is_saved = 1;
06035       caller = trunk_ref->chan->caller;
06036       ast_party_caller_init(&trunk_ref->chan->caller);
06037    }
06038 
06039    dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
06040 
06041    /* Restore saved caller ID */
06042    if (caller_is_saved) {
06043       ast_party_caller_free(&trunk_ref->chan->caller);
06044       trunk_ref->chan->caller = caller;
06045    }
06046 
06047    if (dial_res != AST_DIAL_RESULT_TRYING) {
06048       ast_mutex_lock(args->cond_lock);
06049       ast_cond_signal(args->cond);
06050       ast_mutex_unlock(args->cond_lock);
06051       ast_dial_destroy(dial);
06052       return NULL;
06053    }
06054 
06055    for (;;) {
06056       unsigned int done = 0;
06057       switch ((dial_res = ast_dial_state(dial))) {
06058       case AST_DIAL_RESULT_ANSWERED:
06059          trunk_ref->trunk->chan = ast_dial_answered(dial);
06060       case AST_DIAL_RESULT_HANGUP:
06061       case AST_DIAL_RESULT_INVALID:
06062       case AST_DIAL_RESULT_FAILED:
06063       case AST_DIAL_RESULT_TIMEOUT:
06064       case AST_DIAL_RESULT_UNANSWERED:
06065          done = 1;
06066       case AST_DIAL_RESULT_TRYING:
06067       case AST_DIAL_RESULT_RINGING:
06068       case AST_DIAL_RESULT_PROGRESS:
06069       case AST_DIAL_RESULT_PROCEEDING:
06070          break;
06071       }
06072       if (done)
06073          break;
06074    }
06075 
06076    if (!trunk_ref->trunk->chan) {
06077       ast_mutex_lock(args->cond_lock);
06078       ast_cond_signal(args->cond);
06079       ast_mutex_unlock(args->cond_lock);
06080       ast_dial_join(dial);
06081       ast_dial_destroy(dial);
06082       return NULL;
06083    }
06084 
06085    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06086    ast_set_flag64(&conf_flags, 
06087       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | 
06088       CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
06089    conf = build_conf(conf_name, "", "", 1, 1, 1, trunk_ref->trunk->chan, NULL);
06090 
06091    ast_mutex_lock(args->cond_lock);
06092    ast_cond_signal(args->cond);
06093    ast_mutex_unlock(args->cond_lock);
06094 
06095    if (conf) {
06096       conf_run(trunk_ref->trunk->chan, conf, &conf_flags, NULL);
06097       dispose_conf(conf);
06098       conf = NULL;
06099    }
06100 
06101    /* If the trunk is going away, it is definitely now IDLE. */
06102    sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06103 
06104    trunk_ref->trunk->chan = NULL;
06105    trunk_ref->trunk->on_hold = 0;
06106 
06107    ast_dial_join(dial);
06108    ast_dial_destroy(dial);
06109 
06110    return NULL;
06111 }
06112 
06113 /*! \brief For a given station, choose the highest priority idle trunk
06114  */
06115 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
06116 {
06117    struct sla_trunk_ref *trunk_ref = NULL;
06118 
06119    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06120       if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
06121          break;
06122    }
06123 
06124    return trunk_ref;
06125 }
06126 
06127 static int sla_station_exec(struct ast_channel *chan, const char *data)
06128 {
06129    char *station_name, *trunk_name;
06130    struct sla_station *station;
06131    struct sla_trunk_ref *trunk_ref = NULL;
06132    char conf_name[MAX_CONFNUM];
06133    struct ast_flags64 conf_flags = { 0 };
06134    struct ast_conference *conf;
06135 
06136    if (ast_strlen_zero(data)) {
06137       ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06138       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06139       return 0;
06140    }
06141 
06142    trunk_name = ast_strdupa(data);
06143    station_name = strsep(&trunk_name, "_");
06144 
06145    if (ast_strlen_zero(station_name)) {
06146       ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06147       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06148       return 0;
06149    }
06150 
06151    AST_RWLIST_RDLOCK(&sla_stations);
06152    station = sla_find_station(station_name);
06153    if (station)
06154       ast_atomic_fetchadd_int((int *) &station->ref_count, 1);
06155    AST_RWLIST_UNLOCK(&sla_stations);
06156 
06157    if (!station) {
06158       ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
06159       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06160       sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06161       return 0;
06162    }
06163 
06164    AST_RWLIST_RDLOCK(&sla_trunks);
06165    if (!ast_strlen_zero(trunk_name)) {
06166       trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
06167    } else
06168       trunk_ref = sla_choose_idle_trunk(station);
06169    AST_RWLIST_UNLOCK(&sla_trunks);
06170 
06171    if (!trunk_ref) {
06172       if (ast_strlen_zero(trunk_name))
06173          ast_log(LOG_NOTICE, "No trunks available for call.\n");
06174       else {
06175          ast_log(LOG_NOTICE, "Can't join existing call on trunk "
06176             "'%s' due to access controls.\n", trunk_name);
06177       }
06178       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06179       ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
06180       sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06181       return 0;
06182    }
06183 
06184    if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
06185       if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
06186          sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06187       else {
06188          trunk_ref->state = SLA_TRUNK_STATE_UP;
06189          ast_devstate_changed(AST_DEVICE_INUSE, 
06190             "SLA:%s_%s", station->name, trunk_ref->trunk->name);
06191       }
06192    } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
06193       struct sla_ringing_trunk *ringing_trunk;
06194 
06195       ast_mutex_lock(&sla.lock);
06196       AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06197          if (ringing_trunk->trunk == trunk_ref->trunk) {
06198             AST_LIST_REMOVE_CURRENT(entry);
06199             break;
06200          }
06201       }
06202       AST_LIST_TRAVERSE_SAFE_END
06203       ast_mutex_unlock(&sla.lock);
06204 
06205       if (ringing_trunk) {
06206          answer_trunk_chan(ringing_trunk->trunk->chan);
06207          sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06208 
06209          free(ringing_trunk);
06210 
06211          /* Queue up reprocessing ringing trunks, and then ringing stations again */
06212          sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06213          sla_queue_event(SLA_EVENT_DIAL_STATE);
06214       }
06215    }
06216 
06217    trunk_ref->chan = chan;
06218 
06219    if (!trunk_ref->trunk->chan) {
06220       ast_mutex_t cond_lock;
06221       ast_cond_t cond;
06222       pthread_t dont_care;
06223       struct dial_trunk_args args = {
06224          .trunk_ref = trunk_ref,
06225          .station = station,
06226          .cond_lock = &cond_lock,
06227          .cond = &cond,
06228       };
06229       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06230       /* Create a thread to dial the trunk and dump it into the conference.
06231        * However, we want to wait until the trunk has been dialed and the
06232        * conference is created before continuing on here. */
06233       ast_autoservice_start(chan);
06234       ast_mutex_init(&cond_lock);
06235       ast_cond_init(&cond, NULL);
06236       ast_mutex_lock(&cond_lock);
06237       ast_pthread_create_detached_background(&dont_care, NULL, dial_trunk, &args);
06238       ast_cond_wait(&cond, &cond_lock);
06239       ast_mutex_unlock(&cond_lock);
06240       ast_mutex_destroy(&cond_lock);
06241       ast_cond_destroy(&cond);
06242       ast_autoservice_stop(chan);
06243       if (!trunk_ref->trunk->chan) {
06244          ast_debug(1, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
06245          pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06246          sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06247          trunk_ref->chan = NULL;
06248          ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
06249          sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06250          return 0;
06251       }
06252    }
06253 
06254    if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
06255       trunk_ref->trunk->on_hold) {
06256       trunk_ref->trunk->on_hold = 0;
06257       ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
06258       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06259    }
06260 
06261    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06262    ast_set_flag64(&conf_flags, 
06263       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
06264    ast_answer(chan);
06265    conf = build_conf(conf_name, "", "", 0, 0, 1, chan, NULL);
06266    if (conf) {
06267       conf_run(chan, conf, &conf_flags, NULL);
06268       dispose_conf(conf);
06269       conf = NULL;
06270    }
06271    trunk_ref->chan = NULL;
06272    if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
06273       trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
06274       strncat(conf_name, ",K", sizeof(conf_name) - strlen(conf_name) - 1);
06275       admin_exec(NULL, conf_name);
06276       trunk_ref->trunk->hold_stations = 0;
06277       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06278    }
06279    
06280    pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
06281 
06282    ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
06283    sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06284 
06285    return 0;
06286 }
06287 
06288 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
06289 {
06290    struct sla_trunk_ref *trunk_ref;
06291 
06292    if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
06293       return NULL;
06294 
06295    trunk_ref->trunk = trunk;
06296 
06297    return trunk_ref;
06298 }
06299 
06300 static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
06301 {
06302    struct sla_ringing_trunk *ringing_trunk;
06303 
06304    if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
06305       return NULL;
06306    
06307    ringing_trunk->trunk = trunk;
06308    ringing_trunk->ring_begin = ast_tvnow();
06309 
06310    sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
06311 
06312    ast_mutex_lock(&sla.lock);
06313    AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
06314    ast_mutex_unlock(&sla.lock);
06315 
06316    sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06317 
06318    return ringing_trunk;
06319 }
06320 
06321 enum {
06322    SLA_TRUNK_OPT_MOH = (1 << 0),
06323 };
06324 
06325 enum {
06326    SLA_TRUNK_OPT_ARG_MOH_CLASS = 0,
06327    SLA_TRUNK_OPT_ARG_ARRAY_SIZE = 1,
06328 };
06329 
06330 AST_APP_OPTIONS(sla_trunk_opts, BEGIN_OPTIONS
06331    AST_APP_OPTION_ARG('M', SLA_TRUNK_OPT_MOH, SLA_TRUNK_OPT_ARG_MOH_CLASS),
06332 END_OPTIONS );
06333 
06334 static int sla_trunk_exec(struct ast_channel *chan, const char *data)
06335 {
06336    char conf_name[MAX_CONFNUM];
06337    struct ast_conference *conf;
06338    struct ast_flags64 conf_flags = { 0 };
06339    struct sla_trunk *trunk;
06340    struct sla_ringing_trunk *ringing_trunk;
06341    AST_DECLARE_APP_ARGS(args,
06342       AST_APP_ARG(trunk_name);
06343       AST_APP_ARG(options);
06344    );
06345    char *opts[SLA_TRUNK_OPT_ARG_ARRAY_SIZE] = { NULL, };
06346    struct ast_flags opt_flags = { 0 };
06347    char *parse;
06348 
06349    if (ast_strlen_zero(data)) {
06350       ast_log(LOG_ERROR, "The SLATrunk application requires an argument, the trunk name\n");
06351       return -1;
06352    }
06353 
06354    parse = ast_strdupa(data);
06355    AST_STANDARD_APP_ARGS(args, parse);
06356    if (args.argc == 2) {
06357       if (ast_app_parse_options(sla_trunk_opts, &opt_flags, opts, args.options)) {
06358          ast_log(LOG_ERROR, "Error parsing options for SLATrunk\n");
06359          return -1;
06360       }
06361    }
06362 
06363    AST_RWLIST_RDLOCK(&sla_trunks);
06364    trunk = sla_find_trunk(args.trunk_name);
06365    if (trunk)
06366       ast_atomic_fetchadd_int((int *) &trunk->ref_count, 1);
06367    AST_RWLIST_UNLOCK(&sla_trunks);
06368 
06369    if (!trunk) {
06370       ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", args.trunk_name);
06371       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06372       sla_queue_event(SLA_EVENT_CHECK_RELOAD);  
06373       return 0;
06374    }
06375 
06376    if (trunk->chan) {
06377       ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
06378          args.trunk_name);
06379       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06380       ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
06381       sla_queue_event(SLA_EVENT_CHECK_RELOAD);  
06382       return 0;
06383    }
06384 
06385    trunk->chan = chan;
06386 
06387    if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
06388       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06389       ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
06390       sla_queue_event(SLA_EVENT_CHECK_RELOAD);  
06391       return 0;
06392    }
06393 
06394    snprintf(conf_name, sizeof(conf_name), "SLA_%s", args.trunk_name);
06395    conf = build_conf(conf_name, "", "", 1, 1, 1, chan, NULL);
06396    if (!conf) {
06397       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06398       ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
06399       sla_queue_event(SLA_EVENT_CHECK_RELOAD);  
06400       return 0;
06401    }
06402    ast_set_flag64(&conf_flags, 
06403       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF | CONFFLAG_NO_AUDIO_UNTIL_UP);
06404 
06405    if (ast_test_flag(&opt_flags, SLA_TRUNK_OPT_MOH)) {
06406       ast_indicate(chan, -1);
06407       ast_set_flag64(&conf_flags, CONFFLAG_MOH);
06408    } else
06409       ast_indicate(chan, AST_CONTROL_RINGING);
06410 
06411    conf_run(chan, conf, &conf_flags, opts);
06412    dispose_conf(conf);
06413    conf = NULL;
06414    trunk->chan = NULL;
06415    trunk->on_hold = 0;
06416 
06417    sla_change_trunk_state(trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06418 
06419    if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
06420       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
06421 
06422    /* Remove the entry from the list of ringing trunks if it is still there. */
06423    ast_mutex_lock(&sla.lock);
06424    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06425       if (ringing_trunk->trunk == trunk) {
06426          AST_LIST_REMOVE_CURRENT(entry);
06427          break;
06428       }
06429    }
06430    AST_LIST_TRAVERSE_SAFE_END;
06431    ast_mutex_unlock(&sla.lock);
06432    if (ringing_trunk) {
06433       ast_free(ringing_trunk);
06434       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
06435       /* Queue reprocessing of ringing trunks to make stations stop ringing
06436        * that shouldn't be ringing after this trunk stopped. */
06437       sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06438    }
06439 
06440    ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
06441    sla_queue_event(SLA_EVENT_CHECK_RELOAD);  
06442 
06443    return 0;
06444 }
06445 
06446 static enum ast_device_state sla_state(const char *data)
06447 {
06448    char *buf, *station_name, *trunk_name;
06449    struct sla_station *station;
06450    struct sla_trunk_ref *trunk_ref;
06451    enum ast_device_state res = AST_DEVICE_INVALID;
06452 
06453    trunk_name = buf = ast_strdupa(data);
06454    station_name = strsep(&trunk_name, "_");
06455 
06456    AST_RWLIST_RDLOCK(&sla_stations);
06457    AST_LIST_TRAVERSE(&sla_stations, station, entry) {
06458       if (strcasecmp(station_name, station->name))
06459          continue;
06460       AST_RWLIST_RDLOCK(&sla_trunks);
06461       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06462          if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
06463             break;
06464       }
06465       if (!trunk_ref) {
06466          AST_RWLIST_UNLOCK(&sla_trunks);
06467          break;
06468       }
06469       res = sla_state_to_devstate(trunk_ref->state);
06470       AST_RWLIST_UNLOCK(&sla_trunks);
06471    }
06472    AST_RWLIST_UNLOCK(&sla_stations);
06473 
06474    if (res == AST_DEVICE_INVALID) {
06475       ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
06476          trunk_name, station_name);
06477    }
06478 
06479    return res;
06480 }
06481 
06482 static void destroy_trunk(struct sla_trunk *trunk)
06483 {
06484    struct sla_station_ref *station_ref;
06485 
06486    if (!ast_strlen_zero(trunk->autocontext))
06487       ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
06488 
06489    while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
06490       ast_free(station_ref);
06491 
06492    ast_string_field_free_memory(trunk);
06493    ast_free(trunk);
06494 }
06495 
06496 static void destroy_station(struct sla_station *station)
06497 {
06498    struct sla_trunk_ref *trunk_ref;
06499 
06500    if (!ast_strlen_zero(station->autocontext)) {
06501       AST_RWLIST_RDLOCK(&sla_trunks);
06502       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06503          char exten[AST_MAX_EXTENSION];
06504          char hint[AST_MAX_APP];
06505          snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
06506          snprintf(hint, sizeof(hint), "SLA:%s", exten);
06507          ast_context_remove_extension(station->autocontext, exten, 
06508             1, sla_registrar);
06509          ast_context_remove_extension(station->autocontext, hint, 
06510             PRIORITY_HINT, sla_registrar);
06511       }
06512       AST_RWLIST_UNLOCK(&sla_trunks);
06513    }
06514 
06515    while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
06516       ast_free(trunk_ref);
06517 
06518    ast_string_field_free_memory(station);
06519    ast_free(station);
06520 }
06521 
06522 static void sla_destroy(void)
06523 {
06524    struct sla_trunk *trunk;
06525    struct sla_station *station;
06526 
06527    AST_RWLIST_WRLOCK(&sla_trunks);
06528    while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
06529       destroy_trunk(trunk);
06530    AST_RWLIST_UNLOCK(&sla_trunks);
06531 
06532    AST_RWLIST_WRLOCK(&sla_stations);
06533    while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
06534       destroy_station(station);
06535    AST_RWLIST_UNLOCK(&sla_stations);
06536 
06537    if (sla.thread != AST_PTHREADT_NULL) {
06538       ast_mutex_lock(&sla.lock);
06539       sla.stop = 1;
06540       ast_cond_signal(&sla.cond);
06541       ast_mutex_unlock(&sla.lock);
06542       pthread_join(sla.thread, NULL);
06543    }
06544 
06545    /* Drop any created contexts from the dialplan */
06546    ast_context_destroy(NULL, sla_registrar);
06547 
06548    ast_mutex_destroy(&sla.lock);
06549    ast_cond_destroy(&sla.cond);
06550 }
06551 
06552 static int sla_check_device(const char *device)
06553 {
06554    char *tech, *tech_data;
06555 
06556    tech_data = ast_strdupa(device);
06557    tech = strsep(&tech_data, "/");
06558 
06559    if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
06560       return -1;
06561 
06562    return 0;
06563 }
06564 
06565 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
06566 {
06567    struct sla_trunk *trunk;
06568    struct ast_variable *var;
06569    const char *dev;
06570 
06571    if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
06572       ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
06573       return -1;
06574    }
06575 
06576    if (sla_check_device(dev)) {
06577       ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
06578          cat, dev);
06579       return -1;
06580    }
06581 
06582    if (!(trunk = ast_calloc_with_stringfields(1, struct sla_trunk, 32))) {
06583       return -1;
06584    }
06585 
06586    ast_string_field_set(trunk, name, cat);
06587    ast_string_field_set(trunk, device, dev);
06588 
06589    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
06590       if (!strcasecmp(var->name, "autocontext"))
06591          ast_string_field_set(trunk, autocontext, var->value);
06592       else if (!strcasecmp(var->name, "ringtimeout")) {
06593          if (sscanf(var->value, "%30u", &trunk->ring_timeout) != 1) {
06594             ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
06595                var->value, trunk->name);
06596             trunk->ring_timeout = 0;
06597          }
06598       } else if (!strcasecmp(var->name, "barge"))
06599          trunk->barge_disabled = ast_false(var->value);
06600       else if (!strcasecmp(var->name, "hold")) {
06601          if (!strcasecmp(var->value, "private"))
06602             trunk->hold_access = SLA_HOLD_PRIVATE;
06603          else if (!strcasecmp(var->value, "open"))
06604             trunk->hold_access = SLA_HOLD_OPEN;
06605          else {
06606             ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
06607                var->value, trunk->name);
06608          }
06609       } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
06610          ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
06611             var->name, var->lineno, SLA_CONFIG_FILE);
06612       }
06613    }
06614 
06615    if (!ast_strlen_zero(trunk->autocontext)) {
06616       struct ast_context *context;
06617       context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
06618       if (!context) {
06619          ast_log(LOG_ERROR, "Failed to automatically find or create "
06620             "context '%s' for SLA!\n", trunk->autocontext);
06621          destroy_trunk(trunk);
06622          return -1;
06623       }
06624       if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
06625          NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
06626          ast_log(LOG_ERROR, "Failed to automatically create extension "
06627             "for trunk '%s'!\n", trunk->name);
06628          destroy_trunk(trunk);
06629          return -1;
06630       }
06631    }
06632 
06633    AST_RWLIST_WRLOCK(&sla_trunks);
06634    AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
06635    AST_RWLIST_UNLOCK(&sla_trunks);
06636 
06637    return 0;
06638 }
06639 
06640 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
06641 {
06642    struct sla_trunk *trunk;
06643    struct sla_trunk_ref *trunk_ref;
06644    struct sla_station_ref *station_ref;
06645    char *trunk_name, *options, *cur;
06646 
06647    options = ast_strdupa(var->value);
06648    trunk_name = strsep(&options, ",");
06649    
06650    AST_RWLIST_RDLOCK(&sla_trunks);
06651    AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
06652       if (!strcasecmp(trunk->name, trunk_name))
06653          break;
06654    }
06655 
06656    AST_RWLIST_UNLOCK(&sla_trunks);
06657    if (!trunk) {
06658       ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
06659       return;
06660    }
06661    if (!(trunk_ref = create_trunk_ref(trunk)))
06662       return;
06663    trunk_ref->state = SLA_TRUNK_STATE_IDLE;
06664 
06665    while ((cur = strsep(&options, ","))) {
06666       char *name, *value = cur;
06667       name = strsep(&value, "=");
06668       if (!strcasecmp(name, "ringtimeout")) {
06669          if (sscanf(value, "%30u", &trunk_ref->ring_timeout) != 1) {
06670             ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
06671                "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
06672             trunk_ref->ring_timeout = 0;
06673          }
06674       } else if (!strcasecmp(name, "ringdelay")) {
06675          if (sscanf(value, "%30u", &trunk_ref->ring_delay) != 1) {
06676             ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
06677                "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
06678             trunk_ref->ring_delay = 0;
06679          }
06680       } else {
06681          ast_log(LOG_WARNING, "Invalid option '%s' for "
06682             "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
06683       }
06684    }
06685 
06686    if (!(station_ref = sla_create_station_ref(station))) {
06687       ast_free(trunk_ref);
06688       return;
06689    }
06690    ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
06691    AST_RWLIST_WRLOCK(&sla_trunks);
06692    AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
06693    AST_RWLIST_UNLOCK(&sla_trunks);
06694    AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
06695 }
06696 
06697 static int sla_build_station(struct ast_config *cfg, const char *cat)
06698 {
06699    struct sla_station *station;
06700    struct ast_variable *var;
06701    const char *dev;
06702 
06703    if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
06704       ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
06705       return -1;
06706    }
06707 
06708    if (!(station = ast_calloc_with_stringfields(1, struct sla_station, 32))) {
06709       return -1;
06710    }
06711 
06712    ast_string_field_set(station, name, cat);
06713    ast_string_field_set(station, device, dev);
06714 
06715    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
06716       if (!strcasecmp(var->name, "trunk"))
06717          sla_add_trunk_to_station(station, var);
06718       else if (!strcasecmp(var->name, "autocontext"))
06719          ast_string_field_set(station, autocontext, var->value);
06720       else if (!strcasecmp(var->name, "ringtimeout")) {
06721          if (sscanf(var->value, "%30u", &station->ring_timeout) != 1) {
06722             ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
06723                var->value, station->name);
06724             station->ring_timeout = 0;
06725          }
06726       } else if (!strcasecmp(var->name, "ringdelay")) {
06727          if (sscanf(var->value, "%30u", &station->ring_delay) != 1) {
06728             ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
06729                var->value, station->name);
06730             station->ring_delay = 0;
06731          }
06732       } else if (!strcasecmp(var->name, "hold")) {
06733          if (!strcasecmp(var->value, "private"))
06734             station->hold_access = SLA_HOLD_PRIVATE;
06735          else if (!strcasecmp(var->value, "open"))
06736             station->hold_access = SLA_HOLD_OPEN;
06737          else {
06738             ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
06739                var->value, station->name);
06740          }
06741 
06742       } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
06743          ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
06744             var->name, var->lineno, SLA_CONFIG_FILE);
06745       }
06746    }
06747 
06748    if (!ast_strlen_zero(station->autocontext)) {
06749       struct ast_context *context;
06750       struct sla_trunk_ref *trunk_ref;
06751       context = ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar);
06752       if (!context) {
06753          ast_log(LOG_ERROR, "Failed to automatically find or create "
06754             "context '%s' for SLA!\n", station->autocontext);
06755          destroy_station(station);
06756          return -1;
06757       }
06758       /* The extension for when the handset goes off-hook.
06759        * exten => station1,1,SLAStation(station1) */
06760       if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
06761          NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
06762          ast_log(LOG_ERROR, "Failed to automatically create extension "
06763             "for trunk '%s'!\n", station->name);
06764          destroy_station(station);
06765          return -1;
06766       }
06767       AST_RWLIST_RDLOCK(&sla_trunks);
06768       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06769          char exten[AST_MAX_EXTENSION];
06770          char hint[AST_MAX_APP];
06771          snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
06772          snprintf(hint, sizeof(hint), "SLA:%s", exten);
06773          /* Extension for this line button 
06774           * exten => station1_line1,1,SLAStation(station1_line1) */
06775          if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
06776             NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
06777             ast_log(LOG_ERROR, "Failed to automatically create extension "
06778                "for trunk '%s'!\n", station->name);
06779             destroy_station(station);
06780             return -1;
06781          }
06782          /* Hint for this line button 
06783           * exten => station1_line1,hint,SLA:station1_line1 */
06784          if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
06785             NULL, NULL, hint, NULL, NULL, sla_registrar)) {
06786             ast_log(LOG_ERROR, "Failed to automatically create hint "
06787                "for trunk '%s'!\n", station->name);
06788             destroy_station(station);
06789             return -1;
06790          }
06791       }
06792       AST_RWLIST_UNLOCK(&sla_trunks);
06793    }
06794 
06795    AST_RWLIST_WRLOCK(&sla_stations);
06796    AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
06797    AST_RWLIST_UNLOCK(&sla_stations);
06798 
06799    return 0;
06800 }
06801 
06802 static int sla_load_config(int reload)
06803 {
06804    struct ast_config *cfg;
06805    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06806    const char *cat = NULL;
06807    int res = 0;
06808    const char *val;
06809 
06810    if (!reload) {
06811       ast_mutex_init(&sla.lock);
06812       ast_cond_init(&sla.cond, NULL);
06813    }
06814 
06815    if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags))) {
06816       return 0; /* Treat no config as normal */
06817    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06818       return 0;
06819    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06820       ast_log(LOG_ERROR, "Config file " SLA_CONFIG_FILE " is in an invalid format.  Aborting.\n");
06821       return 0;
06822    }
06823 
06824    if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
06825       sla.attempt_callerid = ast_true(val);
06826 
06827    while ((cat = ast_category_browse(cfg, cat)) && !res) {
06828       const char *type;
06829       if (!strcasecmp(cat, "general"))
06830          continue;
06831       if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
06832          ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
06833             SLA_CONFIG_FILE);
06834          continue;
06835       }
06836       if (!strcasecmp(type, "trunk"))
06837          res = sla_build_trunk(cfg, cat);
06838       else if (!strcasecmp(type, "station"))
06839          res = sla_build_station(cfg, cat);
06840       else {
06841          ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
06842             SLA_CONFIG_FILE, type);
06843       }
06844    }
06845 
06846    ast_config_destroy(cfg);
06847 
06848    /* Even if we don't have any stations, we may after a reload and we need to
06849     * be able to process the SLA_EVENT_RELOAD event in that case */
06850    if (sla.thread == AST_PTHREADT_NULL && (!AST_LIST_EMPTY(&sla_stations) || !AST_LIST_EMPTY(&sla_trunks))) {
06851       ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
06852    }
06853 
06854    return res;
06855 }
06856 
06857 static int acf_meetme_info_eval(const char *keyword, const struct ast_conference *conf)
06858 {
06859    if (!strcasecmp("lock", keyword)) {
06860       return conf->locked;
06861    } else if (!strcasecmp("parties", keyword)) {
06862       return conf->users;
06863    } else if (!strcasecmp("activity", keyword)) {
06864       time_t now;
06865       now = time(NULL);
06866       return (now - conf->start);
06867    } else if (!strcasecmp("dynamic", keyword)) {
06868       return conf->isdynamic;
06869    } else {
06870       return -1;
06871    }
06872 
06873 }
06874 
06875 static int acf_meetme_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06876 {
06877    struct ast_conference *conf;
06878    char *parse;
06879    int result = -2; /* only non-negative numbers valid, -1 is used elsewhere */
06880    AST_DECLARE_APP_ARGS(args,
06881       AST_APP_ARG(keyword);
06882       AST_APP_ARG(confno);
06883    );
06884 
06885    if (ast_strlen_zero(data)) {
06886       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires two arguments\n");
06887       return -1;
06888    }
06889 
06890    parse = ast_strdupa(data);
06891    AST_STANDARD_APP_ARGS(args, parse);
06892 
06893    if (ast_strlen_zero(args.keyword)) {
06894       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a keyword\n");
06895       return -1;
06896    }
06897 
06898    if (ast_strlen_zero(args.confno)) {
06899       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a conference number\n");
06900       return -1;
06901    }
06902 
06903    AST_LIST_LOCK(&confs);
06904    AST_LIST_TRAVERSE(&confs, conf, list) {
06905       if (!strcmp(args.confno, conf->confno)) {
06906          result = acf_meetme_info_eval(args.keyword, conf);
06907          break;
06908       }
06909    }
06910    AST_LIST_UNLOCK(&confs);
06911 
06912    if (result > -1) {
06913       snprintf(buf, len, "%d", result);
06914    } else if (result == -1) {
06915       ast_log(LOG_NOTICE, "Error: invalid keyword: '%s'\n", args.keyword);
06916       snprintf(buf, len, "0");
06917    } else if (result == -2) {
06918       ast_log(LOG_NOTICE, "Error: conference (%s) not found\n", args.confno); 
06919       snprintf(buf, len, "0");
06920    }
06921 
06922    return 0;
06923 }
06924 
06925 
06926 static struct ast_custom_function meetme_info_acf = {
06927    .name = "MEETME_INFO",
06928    .read = acf_meetme_info,
06929 };
06930 
06931 
06932 static int load_config(int reload)
06933 {
06934    load_config_meetme();
06935 
06936    if (reload && sla.thread != AST_PTHREADT_NULL) {
06937       sla_queue_event(SLA_EVENT_RELOAD);
06938       ast_log(LOG_NOTICE, "A reload of the SLA configuration has been requested "
06939          "and will be completed when the system is idle.\n");
06940       return 0;
06941    }
06942    
06943    return sla_load_config(0);
06944 }
06945 
06946 #define MEETME_DATA_EXPORT(MEMBER)              \
06947    MEMBER(ast_conference, confno, AST_DATA_STRING)       \
06948    MEMBER(ast_conference, dahdiconf, AST_DATA_INTEGER)      \
06949    MEMBER(ast_conference, users, AST_DATA_INTEGER)       \
06950    MEMBER(ast_conference, markedusers, AST_DATA_INTEGER)    \
06951    MEMBER(ast_conference, maxusers, AST_DATA_INTEGER)    \
06952    MEMBER(ast_conference, isdynamic, AST_DATA_BOOLEAN)      \
06953    MEMBER(ast_conference, locked, AST_DATA_BOOLEAN)      \
06954    MEMBER(ast_conference, recordingfilename, AST_DATA_STRING)  \
06955    MEMBER(ast_conference, recordingformat, AST_DATA_STRING) \
06956    MEMBER(ast_conference, pin, AST_DATA_PASSWORD)        \
06957    MEMBER(ast_conference, pinadmin, AST_DATA_PASSWORD)      \
06958    MEMBER(ast_conference, start, AST_DATA_TIMESTAMP)     \
06959    MEMBER(ast_conference, endtime, AST_DATA_TIMESTAMP)
06960 
06961 AST_DATA_STRUCTURE(ast_conference, MEETME_DATA_EXPORT);
06962 
06963 #define MEETME_USER_DATA_EXPORT(MEMBER)               \
06964    MEMBER(ast_conf_user, user_no, AST_DATA_INTEGER)      \
06965    MEMBER(ast_conf_user, talking, AST_DATA_BOOLEAN)      \
06966    MEMBER(ast_conf_user, dahdichannel, AST_DATA_BOOLEAN)    \
06967    MEMBER(ast_conf_user, jointime, AST_DATA_TIMESTAMP)      \
06968    MEMBER(ast_conf_user, kicktime, AST_DATA_TIMESTAMP)      \
06969    MEMBER(ast_conf_user, timelimit, AST_DATA_MILLISECONDS)     \
06970    MEMBER(ast_conf_user, play_warning, AST_DATA_MILLISECONDS)  \
06971    MEMBER(ast_conf_user, warning_freq, AST_DATA_MILLISECONDS)
06972 
06973 AST_DATA_STRUCTURE(ast_conf_user, MEETME_USER_DATA_EXPORT);
06974 
06975 static int user_add_provider_cb(void *obj, void *arg, int flags)
06976 {
06977    struct ast_data *data_meetme_user;
06978    struct ast_data *data_meetme_user_channel;
06979    struct ast_data *data_meetme_user_volume;
06980 
06981    struct ast_conf_user *user = obj;
06982    struct ast_data *data_meetme_users = arg;
06983 
06984    data_meetme_user = ast_data_add_node(data_meetme_users, "user");
06985    if (!data_meetme_user) {
06986       return 0;
06987    }
06988    /* user structure */
06989    ast_data_add_structure(ast_conf_user, data_meetme_user, user);
06990 
06991    /* user's channel */
06992    data_meetme_user_channel = ast_data_add_node(data_meetme_user, "channel");
06993    if (!data_meetme_user_channel) {
06994       return 0;
06995    }
06996 
06997    ast_channel_data_add_structure(data_meetme_user_channel, user->chan, 1);
06998 
06999    /* volume structure */
07000    data_meetme_user_volume = ast_data_add_node(data_meetme_user, "listen-volume");
07001    if (!data_meetme_user_volume) {
07002       return 0;
07003    }
07004    ast_data_add_int(data_meetme_user_volume, "desired", user->listen.desired);
07005    ast_data_add_int(data_meetme_user_volume, "actual", user->listen.actual);
07006 
07007    data_meetme_user_volume = ast_data_add_node(data_meetme_user, "talk-volume");
07008    if (!data_meetme_user_volume) {
07009       return 0;
07010    }
07011    ast_data_add_int(data_meetme_user_volume, "desired", user->talk.desired);
07012    ast_data_add_int(data_meetme_user_volume, "actual", user->talk.actual);
07013 
07014    return 0;
07015 }
07016 
07017 /*!
07018  * \internal
07019  * \brief Implements the meetme data provider.
07020  */
07021 static int meetme_data_provider_get(const struct ast_data_search *search,
07022    struct ast_data *data_root)
07023 {
07024    struct ast_conference *cnf;
07025    struct ast_data *data_meetme, *data_meetme_users;
07026 
07027    AST_LIST_LOCK(&confs);
07028    AST_LIST_TRAVERSE(&confs, cnf, list) {
07029       data_meetme = ast_data_add_node(data_root, "meetme");
07030       if (!data_meetme) {
07031          continue;
07032       }
07033 
07034       ast_data_add_structure(ast_conference, data_meetme, cnf);
07035 
07036       if (ao2_container_count(cnf->usercontainer)) {
07037          data_meetme_users = ast_data_add_node(data_meetme, "users");
07038          if (!data_meetme_users) {
07039             ast_data_remove_node(data_root, data_meetme);
07040             continue;
07041          }
07042 
07043          ao2_callback(cnf->usercontainer, OBJ_NODATA, user_add_provider_cb, data_meetme_users); 
07044       }
07045 
07046       if (!ast_data_search_match(search, data_meetme)) {
07047          ast_data_remove_node(data_root, data_meetme);
07048       }
07049    }
07050    AST_LIST_UNLOCK(&confs);
07051 
07052    return 0;
07053 }
07054 
07055 static const struct ast_data_handler meetme_data_provider = {
07056    .version = AST_DATA_HANDLER_VERSION,
07057    .get = meetme_data_provider_get
07058 };
07059 
07060 static const struct ast_data_entry meetme_data_providers[] = {
07061    AST_DATA_ENTRY("asterisk/application/meetme/list", &meetme_data_provider),
07062 };
07063 
07064 #ifdef TEST_FRAMEWORK
07065 AST_TEST_DEFINE(test_meetme_data_provider)
07066 {
07067    struct ast_channel *chan;
07068    struct ast_conference *cnf;
07069    struct ast_data *node;
07070    struct ast_data_query query = {
07071       .path = "/asterisk/application/meetme/list",
07072       .search = "list/meetme/confno=9898"
07073    };
07074 
07075    switch (cmd) {
07076    case TEST_INIT:
07077       info->name = "meetme_get_data_test";
07078       info->category = "/main/data/app_meetme/list/";
07079       info->summary = "Meetme data provider unit test";
07080       info->description =
07081          "Tests whether the Meetme data provider implementation works as expected.";
07082       return AST_TEST_NOT_RUN;
07083    case TEST_EXECUTE:
07084       break;
07085    }
07086 
07087    chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, 0, 0, "MeetMeTest");
07088    if (!chan) {
07089       ast_test_status_update(test, "Channel allocation failed\n");
07090       return AST_TEST_FAIL;
07091    }
07092 
07093    cnf = build_conf("9898", "", "1234", 1, 1, 1, chan, test);
07094    if (!cnf) {
07095       ast_test_status_update(test, "Build of test conference 9898 failed\n");
07096       ast_hangup(chan);
07097       return AST_TEST_FAIL;
07098    }
07099 
07100    node = ast_data_get(&query);
07101    if (!node) {
07102       ast_test_status_update(test, "Data query for test conference 9898 failed\n");
07103       dispose_conf(cnf);
07104       ast_hangup(chan);
07105       return AST_TEST_FAIL;
07106    }
07107 
07108    if (strcmp(ast_data_retrieve_string(node, "meetme/confno"), "9898")) {
07109       ast_test_status_update(test, "Query returned the wrong conference\n");
07110       dispose_conf(cnf);
07111       ast_hangup(chan);
07112       ast_data_free(node);
07113       return AST_TEST_FAIL;
07114    }
07115 
07116    ast_data_free(node);
07117    dispose_conf(cnf);
07118    ast_hangup(chan);
07119 
07120    return AST_TEST_PASS;
07121 }
07122 #endif
07123 
07124 static int unload_module(void)
07125 {
07126    int res = 0;
07127    
07128    ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
07129    res = ast_manager_unregister("MeetmeMute");
07130    res |= ast_manager_unregister("MeetmeUnmute");
07131    res |= ast_manager_unregister("MeetmeList");
07132    res |= ast_unregister_application(app4);
07133    res |= ast_unregister_application(app3);
07134    res |= ast_unregister_application(app2);
07135    res |= ast_unregister_application(app);
07136    res |= ast_unregister_application(slastation_app);
07137    res |= ast_unregister_application(slatrunk_app);
07138 
07139 #ifdef TEST_FRAMEWORK
07140    AST_TEST_UNREGISTER(test_meetme_data_provider);
07141 #endif
07142    ast_data_unregister(NULL);
07143 
07144    ast_devstate_prov_del("Meetme");
07145    ast_devstate_prov_del("SLA");
07146    
07147    sla_destroy();
07148    
07149    res |= ast_custom_function_unregister(&meetme_info_acf);
07150    ast_unload_realtime("meetme");
07151 
07152    return res;
07153 }
07154 
07155 
07156 
07157 static int load_module(void)
07158 {
07159    int res = 0;
07160 
07161    res |= load_config(0);
07162 
07163    ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
07164    res |= ast_manager_register_xml("MeetmeMute", EVENT_FLAG_CALL, action_meetmemute);
07165    res |= ast_manager_register_xml("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute);
07166    res |= ast_manager_register_xml("MeetmeList", EVENT_FLAG_REPORTING, action_meetmelist);
07167    res |= ast_register_application_xml(app4, channel_admin_exec);
07168    res |= ast_register_application_xml(app3, admin_exec);
07169    res |= ast_register_application_xml(app2, count_exec);
07170    res |= ast_register_application_xml(app, conf_exec);
07171    res |= ast_register_application_xml(slastation_app, sla_station_exec);
07172    res |= ast_register_application_xml(slatrunk_app, sla_trunk_exec);
07173 
07174 #ifdef TEST_FRAMEWORK
07175    AST_TEST_REGISTER(test_meetme_data_provider);
07176 #endif
07177    ast_data_register_multiple(meetme_data_providers, ARRAY_LEN(meetme_data_providers));
07178 
07179    res |= ast_devstate_prov_add("Meetme", meetmestate);
07180    res |= ast_devstate_prov_add("SLA", sla_state);
07181 
07182    res |= ast_custom_function_register(&meetme_info_acf);
07183    ast_realtime_require_field("meetme", "confno", RQ_UINTEGER2, 3, "members", RQ_UINTEGER1, 3, NULL);
07184 
07185    return res;
07186 }
07187 
07188 static int reload(void)
07189 {
07190    ast_unload_realtime("meetme");
07191    return load_config(1);
07192 }
07193 
07194 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "MeetMe conference bridge",
07195       .load = load_module,
07196       .unload = unload_module,
07197       .reload = reload,
07198       .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
07199           );
07200