Mon Sep 20 2010 00:19:47

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