Sun Oct 16 2011 08:41:27

Asterisk developer's documentation


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