Wed Nov 28 2012 04:18:00

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