Wed Apr 6 11:29:38 2011

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

Generated on Wed Apr 6 11:29:38 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7