Thu Apr 28 2011 17:13:28

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