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

Generated on Mon Jun 27 16:50:46 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7