Tue Aug 20 16:34:21 2013

Asterisk developer's documentation


app_meetme.c

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

Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1