Mon Apr 28 2014 10:05:34

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