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