Tue Aug 24 2010 19:41:25

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