Thu Sep 7 01:02:50 2017

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: 419129 $")
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  */
02549 static void meetme_menu_admin_extended(enum menu_modes *menu_mode, int *dtmf,
02550    struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan,
02551    struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size)
02552 {
02553    int keepplaying;
02554    int playednamerec;
02555    int res;
02556    struct ao2_iterator user_iter;
02557    struct ast_conf_user *usr = NULL;
02558 
02559    switch(*dtmf) {
02560    case '1': /* *81 Roll call */
02561       keepplaying = 1;
02562       playednamerec = 0;
02563       if (conf->users == 1) {
02564          if (keepplaying && !ast_streamfile(chan, "conf-onlyperson", chan->language)) {
02565             res = ast_waitstream(chan, AST_DIGIT_ANY);
02566             ast_stopstream(chan);
02567             if (res > 0) {
02568                keepplaying = 0;
02569             }
02570          }
02571       } else if (conf->users == 2) {
02572          if (keepplaying && !ast_streamfile(chan, "conf-onlyone", chan->language)) {
02573             res = ast_waitstream(chan, AST_DIGIT_ANY);
02574             ast_stopstream(chan);
02575             if (res > 0) {
02576                keepplaying = 0;
02577             }
02578          }
02579       } else {
02580          if (keepplaying && !ast_streamfile(chan, "conf-thereare", chan->language)) {
02581             res = ast_waitstream(chan, AST_DIGIT_ANY);
02582             ast_stopstream(chan);
02583             if (res > 0) {
02584                keepplaying = 0;
02585             }
02586          }
02587          if (keepplaying) {
02588             res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
02589             ast_stopstream(chan);
02590             if (res > 0) {
02591                keepplaying = 0;
02592             }
02593          }
02594          if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
02595             res = ast_waitstream(chan, AST_DIGIT_ANY);
02596             ast_stopstream(chan);
02597             if (res > 0) {
02598                keepplaying = 0;
02599             }
02600          }
02601       }
02602       user_iter = ao2_iterator_init(conf->usercontainer, 0);
02603       while((usr = ao2_iterator_next(&user_iter))) {
02604          if (ast_fileexists(usr->namerecloc, NULL, NULL)) {
02605             if (keepplaying && !ast_streamfile(chan, usr->namerecloc, chan->language)) {
02606                res = ast_waitstream(chan, AST_DIGIT_ANY);
02607                ast_stopstream(chan);
02608                if (res > 0) {
02609                   keepplaying = 0;
02610                }
02611             }
02612             playednamerec = 1;
02613          }
02614          ao2_ref(usr, -1);
02615       }
02616       ao2_iterator_destroy(&user_iter);
02617       if (keepplaying && playednamerec && !ast_streamfile(chan, "conf-roll-callcomplete", chan->language)) {
02618          res = ast_waitstream(chan, AST_DIGIT_ANY);
02619          ast_stopstream(chan);
02620          if (res > 0) {
02621             keepplaying = 0;
02622          }
02623       }
02624 
02625       *menu_mode = MENU_DISABLED;
02626       break;
02627 
02628    case '2': /* *82 Eject all non-admins */
02629       if (conf->users == 1) {
02630          if(!ast_streamfile(chan, "conf-errormenu", chan->language)) {
02631             ast_waitstream(chan, "");
02632          }
02633       } else {
02634          ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_kickme_cb, &conf);
02635       }
02636       ast_stopstream(chan);
02637       *menu_mode = MENU_DISABLED;
02638       break;
02639 
02640    case '3': /* *83 (Admin) mute/unmute all non-admins */
02641       if(conf->gmuted) {
02642          conf->gmuted = 0;
02643          ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, &conf);
02644          if (!ast_streamfile(chan, "conf-now-unmuted", chan->language)) {
02645             ast_waitstream(chan, "");
02646          }
02647       } else {
02648          conf->gmuted = 1;
02649          ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_muted_cb, &conf);
02650          if (!ast_streamfile(chan, "conf-now-muted", chan->language)) {
02651             ast_waitstream(chan, "");
02652          }
02653       }
02654       ast_stopstream(chan);
02655       *menu_mode = MENU_DISABLED;
02656       break;
02657 
02658    case '4': /* *84 Record conference */
02659       if (conf->recording != MEETME_RECORD_ACTIVE) {
02660          ast_set_flag64(confflags, CONFFLAG_RECORDCONF);
02661          if (!conf->recordingfilename) {
02662             const char *var;
02663             ast_channel_lock(chan);
02664             if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
02665                conf->recordingfilename = ast_strdup(var);
02666             }
02667             if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
02668                conf->recordingformat = ast_strdup(var);
02669             }
02670             ast_channel_unlock(chan);
02671             if (!conf->recordingfilename) {
02672                snprintf(recordingtmp, recordingtmp_size, "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
02673                conf->recordingfilename = ast_strdup(recordingtmp);
02674             }
02675             if (!conf->recordingformat) {
02676                conf->recordingformat = ast_strdup("wav");
02677             }
02678             ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
02679             conf->confno, conf->recordingfilename, conf->recordingformat);
02680          }
02681 
02682          ast_mutex_lock(&conf->recordthreadlock);
02683          if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL)))) {
02684             struct dahdi_confinfo dahdic;
02685 
02686             ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
02687             ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
02688             dahdic.chan = 0;
02689             dahdic.confno = conf->dahdiconf;
02690             dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
02691             if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
02692                ast_log(LOG_WARNING, "Error starting listen channel\n");
02693                ast_hangup(conf->lchan);
02694                conf->lchan = NULL;
02695             } else {
02696                ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
02697             }
02698          }
02699          ast_mutex_unlock(&conf->recordthreadlock);
02700          if (!ast_streamfile(chan, "conf-now-recording", chan->language)) {
02701             ast_waitstream(chan, "");
02702          }
02703       }
02704 
02705       ast_stopstream(chan);
02706       *menu_mode = MENU_DISABLED;
02707       break;
02708 
02709    case '8': /* *88 Exit the menu and return to the conference... without an error message */
02710       ast_stopstream(chan);
02711       *menu_mode = MENU_DISABLED;
02712       break;
02713 
02714    default:
02715       if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
02716          ast_waitstream(chan, "");
02717       }
02718       ast_stopstream(chan);
02719       *menu_mode = MENU_DISABLED;
02720       break;
02721    }
02722 }
02723 
02724 /*! \internal
02725  * \brief Processes menu options for the various menu types (accessible through the 's' option for app_meetme)
02726  *
02727  * \param menu_mode a pointer to the currently active menu_mode.
02728  * \param dtmf a pointer to the dtmf value currently being processed against the menu.
02729  * \param conf the active conference for which the user has called the menu from.
02730  * \param confflags flags used by conf for various options
02731  * \param chan ast_channel belonging to the user who called the menu
02732  * \param user which meetme conference user invoked the menu
02733  * \param recordingtmp character buffer which may hold the name of the conference recording file
02734  */
02735 
02736 static void meetme_menu(enum menu_modes *menu_mode, int *dtmf,
02737    struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan,
02738    struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size)
02739 {
02740    switch (*menu_mode) {
02741    case MENU_DISABLED:
02742       break;
02743    case MENU_NORMAL:
02744       meetme_menu_normal(menu_mode, dtmf, conf, confflags, chan, user);
02745       break;
02746    case MENU_ADMIN:
02747       meetme_menu_admin(menu_mode, dtmf, conf, confflags, chan, user);
02748       /* Admin Menu is capable of branching into another menu, in which case it will reset dtmf and change the menu mode. */
02749       if (*menu_mode != MENU_ADMIN_EXTENDED || (*dtmf <= 0)) {
02750          break;
02751       }
02752    case MENU_ADMIN_EXTENDED:
02753       meetme_menu_admin_extended(menu_mode, dtmf, conf, confflags, chan, user,
02754          recordingtmp, recordingtmp_size);
02755       break;
02756    }
02757 }
02758 
02759 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
02760 {
02761    struct ast_conf_user *user = NULL;
02762    int fd;
02763    struct dahdi_confinfo dahdic, dahdic_empty;
02764    struct ast_frame *f;
02765    struct ast_channel *c;
02766    struct ast_frame fr;
02767    int outfd;
02768    int ms;
02769    int nfds;
02770    int res;
02771    int retrydahdi;
02772    int origfd;
02773    int musiconhold = 0, mohtempstopped = 0;
02774    int firstpass = 0;
02775    int lastmarked = 0;
02776    int currentmarked = 0;
02777    int ret = -1;
02778    int x;
02779    enum menu_modes menu_mode = MENU_DISABLED;
02780    int talkreq_manager = 0;
02781    int using_pseudo = 0;
02782    int duration = 20;
02783    int sent_event = 0;
02784    int checked = 0;
02785    int announcement_played = 0;
02786    struct timeval now;
02787    struct ast_dsp *dsp = NULL;
02788    struct ast_app *agi_app;
02789    char *agifile, *mod_speex;
02790    const char *agifiledefault = "conf-background.agi", *tmpvar;
02791    char meetmesecs[30] = "";
02792    char exitcontext[AST_MAX_CONTEXT] = "";
02793    char recordingtmp[AST_MAX_EXTENSION] = "";
02794    char members[10] = "";
02795    int dtmf = 0, opt_waitmarked_timeout = 0;
02796    time_t timeout = 0;
02797    struct dahdi_bufferinfo bi;
02798    char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
02799    char *buf = __buf + AST_FRIENDLY_OFFSET;
02800    char *exitkeys = NULL;
02801    unsigned int calldurationlimit = 0;
02802    long timelimit = 0;
02803    long play_warning = 0;
02804    long warning_freq = 0;
02805    const char *warning_sound = NULL;
02806    const char *end_sound = NULL;
02807    char *parse;   
02808    long time_left_ms = 0;
02809    struct timeval nexteventts = { 0, };
02810    int to;
02811    int setusercount = 0;
02812    int confsilence = 0, totalsilence = 0;
02813 
02814    if (!(user = ao2_alloc(sizeof(*user), NULL))) {
02815       return ret;
02816    }
02817 
02818    /* Possible timeout waiting for marked user */
02819    if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
02820       !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
02821       (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
02822       (opt_waitmarked_timeout > 0)) {
02823       timeout = time(NULL) + opt_waitmarked_timeout;
02824    }
02825       
02826    if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
02827       calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
02828       ast_verb(3, "Setting call duration limit to %u seconds.\n", calldurationlimit);
02829    }
02830    
02831    if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
02832       char *limit_str, *warning_str, *warnfreq_str;
02833       const char *var;
02834  
02835       parse = optargs[OPT_ARG_DURATION_LIMIT];
02836       limit_str = strsep(&parse, ":");
02837       warning_str = strsep(&parse, ":");
02838       warnfreq_str = parse;
02839  
02840       timelimit = atol(limit_str);
02841       if (warning_str)
02842          play_warning = atol(warning_str);
02843       if (warnfreq_str)
02844          warning_freq = atol(warnfreq_str);
02845  
02846       if (!timelimit) {
02847          timelimit = play_warning = warning_freq = 0;
02848          warning_sound = NULL;
02849       } else if (play_warning > timelimit) {       
02850          if (!warning_freq) {
02851             play_warning = 0;
02852          } else {
02853             while (play_warning > timelimit)
02854                play_warning -= warning_freq;
02855             if (play_warning < 1)
02856                play_warning = warning_freq = 0;
02857          }
02858       }
02859 
02860       ast_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
02861       if (play_warning) {
02862          ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
02863       }
02864       if (warning_freq) {
02865          ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
02866       }
02867 
02868       ast_channel_lock(chan);
02869       if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
02870          var = ast_strdupa(var);
02871       }
02872       ast_channel_unlock(chan);
02873 
02874       warning_sound = var ? var : "timeleft";
02875       
02876       ast_channel_lock(chan);
02877       if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
02878          var = ast_strdupa(var);
02879       }
02880       ast_channel_unlock(chan);
02881       
02882       end_sound = var ? var : NULL;
02883          
02884       /* undo effect of S(x) in case they are both used */
02885       calldurationlimit = 0;
02886       /* more efficient do it like S(x) does since no advanced opts */
02887       if (!play_warning && !end_sound && timelimit) { 
02888          calldurationlimit = timelimit / 1000;
02889          timelimit = play_warning = warning_freq = 0;
02890       } else {
02891          ast_debug(2, "Limit Data for this call:\n");
02892          ast_debug(2, "- timelimit     = %ld\n", timelimit);
02893          ast_debug(2, "- play_warning  = %ld\n", play_warning);
02894          ast_debug(2, "- warning_freq  = %ld\n", warning_freq);
02895          ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
02896          ast_debug(2, "- end_sound     = %s\n", end_sound ? end_sound : "UNDEF");
02897       }
02898    }
02899 
02900    /* Get exit keys */
02901    if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
02902       if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
02903          exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
02904       else
02905          exitkeys = ast_strdupa("#"); /* Default */
02906    }
02907    
02908    if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
02909       if (!conf->recordingfilename) {
02910          const char *var;
02911          ast_channel_lock(chan);
02912          if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
02913             conf->recordingfilename = ast_strdup(var);
02914          }
02915          if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
02916             conf->recordingformat = ast_strdup(var);
02917          }
02918          ast_channel_unlock(chan);
02919          if (!conf->recordingfilename) {
02920             snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
02921             conf->recordingfilename = ast_strdup(recordingtmp);
02922          }
02923          if (!conf->recordingformat) {
02924             conf->recordingformat = ast_strdup("wav");
02925          }
02926          ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
02927                 conf->confno, conf->recordingfilename, conf->recordingformat);
02928       }
02929    }
02930 
02931    ast_mutex_lock(&conf->recordthreadlock);
02932    if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
02933       ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL)))) {
02934       ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
02935       ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
02936       dahdic.chan = 0;
02937       dahdic.confno = conf->dahdiconf;
02938       dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
02939       if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
02940          ast_log(LOG_WARNING, "Error starting listen channel\n");
02941          ast_hangup(conf->lchan);
02942          conf->lchan = NULL;
02943       } else {
02944          ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
02945       }
02946    }
02947    ast_mutex_unlock(&conf->recordthreadlock);
02948 
02949    ast_mutex_lock(&conf->announcethreadlock);
02950    if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
02951       (ast_test_flag64(confflags, CONFFLAG_INTROUSER) || ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))) {
02952       ast_mutex_init(&conf->announcelistlock);
02953       AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
02954       ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
02955    }
02956    ast_mutex_unlock(&conf->announcethreadlock);
02957 
02958    time(&user->jointime);
02959    
02960    user->timelimit = timelimit;
02961    user->play_warning = play_warning;
02962    user->warning_freq = warning_freq;
02963    user->warning_sound = warning_sound;
02964    user->end_sound = end_sound;  
02965    
02966    if (calldurationlimit > 0) {
02967       time(&user->kicktime);
02968       user->kicktime = user->kicktime + calldurationlimit;
02969    }
02970    
02971    if (ast_tvzero(user->start_time))
02972       user->start_time = ast_tvnow();
02973    time_left_ms = user->timelimit;
02974    
02975    if (user->timelimit) {
02976       nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
02977       nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
02978    }
02979 
02980    if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
02981       /* Sorry, but this conference is locked! */  
02982       if (!ast_streamfile(chan, "conf-locked", chan->language))
02983          ast_waitstream(chan, "");
02984       goto outrun;
02985    }
02986 
02987       ast_mutex_lock(&conf->playlock);
02988 
02989    if (rt_schedule && conf->maxusers) {
02990       if (conf->users >= conf->maxusers) {
02991          /* Sorry, but this confernce has reached the participant limit! */   
02992          ast_mutex_unlock(&conf->playlock);
02993          if (!ast_streamfile(chan, "conf-full", chan->language))
02994             ast_waitstream(chan, "");
02995          goto outrun;
02996       }
02997    }
02998 
02999    ao2_lock(conf->usercontainer);
03000    ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
03001    user->user_no++;
03002    ao2_link(conf->usercontainer, user);
03003    ao2_unlock(conf->usercontainer);
03004 
03005    user->chan = chan;
03006    user->userflags = *confflags;
03007    user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
03008    user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
03009    user->talking = -1;
03010 
03011    ast_mutex_unlock(&conf->playlock);
03012 
03013    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
03014       ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))) {
03015       char destdir[PATH_MAX];
03016 
03017       snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
03018 
03019       if (ast_mkdir(destdir, 0777) != 0) {
03020          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
03021          goto outrun;
03022       }
03023 
03024       snprintf(user->namerecloc, sizeof(user->namerecloc),
03025           "%s/meetme-username-%s-%d", destdir,
03026           conf->confno, user->user_no);
03027       if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))
03028          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);
03029       else
03030          res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
03031       if (res == -1)
03032          goto outrun;
03033    }
03034 
03035    ast_mutex_lock(&conf->playlock);
03036 
03037    if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
03038       conf->markedusers++;
03039    conf->users++;
03040    if (rt_log_members) {
03041       /* Update table */
03042       snprintf(members, sizeof(members), "%d", conf->users);
03043       ast_realtime_require_field("meetme",
03044          "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
03045          "members", RQ_UINTEGER1, strlen(members),
03046          NULL);
03047       ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
03048    }
03049    setusercount = 1;
03050 
03051    /* This device changed state now - if this is the first user */
03052    if (conf->users == 1)
03053       ast_devstate_changed(AST_DEVICE_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
03054 
03055    ast_mutex_unlock(&conf->playlock);
03056 
03057    /* return the unique ID of the conference */
03058    pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
03059 
03060    if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
03061       ast_channel_lock(chan);
03062       if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
03063          ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
03064       } else if (!ast_strlen_zero(chan->macrocontext)) {
03065          ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
03066       } else {
03067          ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
03068       }
03069       ast_channel_unlock(chan);
03070    }
03071 
03072    /* Play an arbitrary intro message */
03073    if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
03074          !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
03075       if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], chan->language)) {
03076          ast_waitstream(chan, "");
03077       }
03078    }
03079 
03080    if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
03081       if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
03082          if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
03083             ast_waitstream(chan, "");
03084       if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
03085          if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
03086             ast_waitstream(chan, "");
03087    }
03088 
03089    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) &&
03090       conf->users > 1) {
03091       int keepplaying = 1;
03092 
03093       if (conf->users == 2) { 
03094          if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
03095             res = ast_waitstream(chan, AST_DIGIT_ANY);
03096             ast_stopstream(chan);
03097             if (res > 0)
03098                keepplaying = 0;
03099             else if (res == -1)
03100                goto outrun;
03101          }
03102       } else { 
03103          if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
03104             res = ast_waitstream(chan, AST_DIGIT_ANY);
03105             ast_stopstream(chan);
03106             if (res > 0)
03107                keepplaying = 0;
03108             else if (res == -1)
03109                goto outrun;
03110          }
03111          if (keepplaying) {
03112             res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
03113             if (res > 0)
03114                keepplaying = 0;
03115             else if (res == -1)
03116                goto outrun;
03117          }
03118          if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
03119             res = ast_waitstream(chan, AST_DIGIT_ANY);
03120             ast_stopstream(chan);
03121             if (res > 0)
03122                keepplaying = 0;
03123             else if (res == -1) 
03124                goto outrun;
03125          }
03126       }
03127    }
03128 
03129    if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
03130       /* We're leaving this alone until the state gets changed to up */
03131       ast_indicate(chan, -1);
03132    }
03133 
03134    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
03135       ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
03136       goto outrun;
03137    }
03138 
03139    if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
03140       ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
03141       goto outrun;
03142    }
03143 
03144    /* Reduce background noise from each participant */
03145    if (!ast_test_flag64(confflags, CONFFLAG_DONT_DENOISE) &&
03146       (mod_speex = ast_module_helper("", "func_speex", 0, 0, 0, 0))) {
03147       ast_free(mod_speex);
03148       ast_func_write(chan, "DENOISE(rx)", "on");
03149    }
03150 
03151    retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
03152    user->dahdichannel = !retrydahdi;
03153 
03154  dahdiretry:
03155    origfd = chan->fds[0];
03156    if (retrydahdi) {
03157       /* open pseudo in non-blocking mode */
03158       fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
03159       if (fd < 0) {
03160          ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
03161          goto outrun;
03162       }
03163       using_pseudo = 1;
03164       /* Setup buffering information */
03165       memset(&bi, 0, sizeof(bi));
03166       bi.bufsize = CONF_SIZE / 2;
03167       bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
03168       bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
03169       bi.numbufs = audio_buffers;
03170       if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
03171          ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
03172          close(fd);
03173          goto outrun;
03174       }
03175       x = 1;
03176       if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
03177          ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
03178          close(fd);
03179          goto outrun;
03180       }
03181       nfds = 1;
03182    } else {
03183       /* XXX Make sure we're not running on a pseudo channel XXX */
03184       fd = chan->fds[0];
03185       nfds = 0;
03186    }
03187    memset(&dahdic, 0, sizeof(dahdic));
03188    memset(&dahdic_empty, 0, sizeof(dahdic_empty));
03189    /* Check to see if we're in a conference... */
03190    dahdic.chan = 0;  
03191    if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
03192       ast_log(LOG_WARNING, "Error getting conference\n");
03193       close(fd);
03194       goto outrun;
03195    }
03196    if (dahdic.confmode) {
03197       /* Whoa, already in a conference...  Retry... */
03198       if (!retrydahdi) {
03199          ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
03200          retrydahdi = 1;
03201          goto dahdiretry;
03202       }
03203    }
03204    memset(&dahdic, 0, sizeof(dahdic));
03205    /* Add us to the conference */
03206    dahdic.chan = 0;  
03207    dahdic.confno = conf->dahdiconf;
03208 
03209    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
03210       ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
03211       struct announce_listitem *item;
03212       if (!(item = ao2_alloc(sizeof(*item), NULL)))
03213          goto outrun;
03214       ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
03215       ast_copy_string(item->language, chan->language, sizeof(item->language));
03216       item->confchan = conf->chan;
03217       item->confusers = conf->users;
03218       item->announcetype = CONF_HASJOIN;
03219       ast_mutex_lock(&conf->announcelistlock);
03220       ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
03221       AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
03222       ast_cond_signal(&conf->announcelist_addition);
03223       ast_mutex_unlock(&conf->announcelistlock);
03224 
03225       while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
03226          ;
03227       }
03228       ao2_ref(item, -1);
03229    }
03230 
03231    if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && !conf->markedusers)
03232       dahdic.confmode = DAHDI_CONF_CONF;
03233    else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
03234       dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
03235    else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
03236       dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
03237    else 
03238       dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
03239 
03240    if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03241       ast_log(LOG_WARNING, "Error setting conference\n");
03242       close(fd);
03243       goto outrun;
03244    }
03245    ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
03246 
03247    if (!sent_event) {
03248       ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
03249          "Channel: %s\r\n"
03250          "Uniqueid: %s\r\n"
03251          "Meetme: %s\r\n"
03252          "Usernum: %d\r\n"
03253          "CallerIDnum: %s\r\n"
03254          "CallerIDname: %s\r\n"
03255          "ConnectedLineNum: %s\r\n"
03256          "ConnectedLineName: %s\r\n",
03257          chan->name, chan->uniqueid, conf->confno,
03258          user->user_no,
03259          S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
03260          S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<unknown>"),
03261          S_COR(user->chan->connected.id.number.valid, user->chan->connected.id.number.str, "<unknown>"),
03262          S_COR(user->chan->connected.id.name.valid, user->chan->connected.id.name.str, "<unknown>")
03263          );
03264       sent_event = 1;
03265    }
03266 
03267    if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
03268       !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03269       firstpass = 1;
03270       if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
03271          if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
03272             (conf->markedusers >= 1))) {
03273             conf_play(chan, conf, ENTER);
03274          }
03275    }
03276 
03277    conf_flush(fd, chan);
03278 
03279    if (dsp)
03280       ast_dsp_free(dsp);
03281 
03282    if (!(dsp = ast_dsp_new())) {
03283       ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
03284       res = -1;
03285    }
03286 
03287    if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
03288       /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
03289          or use default filename of conf-background.agi */
03290 
03291       ast_channel_lock(chan);
03292       if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
03293          agifile = ast_strdupa(tmpvar);
03294       } else {
03295          agifile = ast_strdupa(agifiledefault);
03296       }
03297       ast_channel_unlock(chan);
03298       
03299       if (user->dahdichannel) {
03300          /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
03301          x = 1;
03302          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03303       }
03304       /* Find a pointer to the agi app and execute the script */
03305       agi_app = pbx_findapp("agi");
03306       if (agi_app) {
03307          ret = pbx_exec(chan, agi_app, agifile);
03308       } else {
03309          ast_log(LOG_WARNING, "Could not find application (agi)\n");
03310          ret = -2;
03311       }
03312       if (user->dahdichannel) {
03313          /*  Remove CONFMUTE mode on DAHDI channel */
03314          x = 0;
03315          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03316       }
03317    } else {
03318       int lastusers = conf->users;
03319       if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
03320          /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
03321          x = 1;
03322          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03323       }
03324 
03325       for (;;) {
03326          int menu_was_active = 0;
03327 
03328          outfd = -1;
03329          ms = -1;
03330          now = ast_tvnow();
03331 
03332          if (rt_schedule && conf->endtime) {
03333             char currenttime[32];
03334             long localendtime = 0;
03335             int extended = 0;
03336             struct ast_tm tm;
03337             struct ast_variable *var, *origvar;
03338             struct timeval tmp;
03339 
03340             if (now.tv_sec % 60 == 0) {
03341                if (!checked) {
03342                   ast_localtime(&now, &tm, NULL);
03343                   ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
03344                   var = origvar = ast_load_realtime("meetme", "confno",
03345                      conf->confno, "starttime <=", currenttime,
03346                       "endtime >=", currenttime, NULL);
03347 
03348                   for ( ; var; var = var->next) {
03349                      if (!strcasecmp(var->name, "endtime")) {
03350                         struct ast_tm endtime_tm;
03351                         ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
03352                         tmp = ast_mktime(&endtime_tm, NULL);
03353                         localendtime = tmp.tv_sec;
03354                      }
03355                   }
03356                   ast_variables_destroy(origvar);
03357 
03358                   /* A conference can be extended from the
03359                      Admin/User menu or by an external source */
03360                   if (localendtime > conf->endtime){
03361                      conf->endtime = localendtime;
03362                      extended = 1;
03363                   }
03364 
03365                   if (conf->endtime && (now.tv_sec >= conf->endtime)) {
03366                      ast_verbose("Quitting time...\n");
03367                      goto outrun;
03368                   }
03369 
03370                   if (!announcement_played && conf->endalert) {
03371                      if (now.tv_sec + conf->endalert >= conf->endtime) {
03372                         if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
03373                            ast_waitstream(chan, "");
03374                         ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
03375                         if (!ast_streamfile(chan, "minutes", chan->language))
03376                            ast_waitstream(chan, "");
03377                         if (musiconhold) {
03378                            conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03379                         }
03380                         announcement_played = 1;
03381                      }
03382                   }
03383 
03384                   if (extended) {
03385                      announcement_played = 0;
03386                   }
03387 
03388                   checked = 1;
03389                }
03390             } else {
03391                checked = 0;
03392             }
03393          }
03394 
03395          if (user->kicktime && (user->kicktime <= now.tv_sec)) {
03396             if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03397                ret = 0;
03398             } else {
03399                ret = -1;
03400             }
03401             break;
03402          }
03403   
03404          to = -1;
03405          if (user->timelimit) {
03406             int minutes = 0, seconds = 0, remain = 0;
03407  
03408             to = ast_tvdiff_ms(nexteventts, now);
03409             if (to < 0) {
03410                to = 0;
03411             }
03412             time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
03413             if (time_left_ms < to) {
03414                to = time_left_ms;
03415             }
03416    
03417             if (time_left_ms <= 0) {
03418                if (user->end_sound) {                 
03419                   res = ast_streamfile(chan, user->end_sound, chan->language);
03420                   res = ast_waitstream(chan, "");
03421                }
03422                if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03423                   ret = 0;
03424                } else {
03425                   ret = -1;
03426                }
03427                break;
03428             }
03429             
03430             if (!to) {
03431                if (time_left_ms >= 5000) {                  
03432                   
03433                   remain = (time_left_ms + 500) / 1000;
03434                   if (remain / 60 >= 1) {
03435                      minutes = remain / 60;
03436                      seconds = remain % 60;
03437                   } else {
03438                      seconds = remain;
03439                   }
03440                   
03441                   /* force the time left to round up if appropriate */
03442                   if (user->warning_sound && user->play_warning) {
03443                      if (!strcmp(user->warning_sound, "timeleft")) {
03444                         
03445                         res = ast_streamfile(chan, "vm-youhave", chan->language);
03446                         res = ast_waitstream(chan, "");
03447                         if (minutes) {
03448                            res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
03449                            res = ast_streamfile(chan, "queue-minutes", chan->language);
03450                            res = ast_waitstream(chan, "");
03451                         }
03452                         if (seconds) {
03453                            res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
03454                            res = ast_streamfile(chan, "queue-seconds", chan->language);
03455                            res = ast_waitstream(chan, "");
03456                         }
03457                      } else {
03458                         res = ast_streamfile(chan, user->warning_sound, chan->language);
03459                         res = ast_waitstream(chan, "");
03460                      }
03461                      if (musiconhold) {
03462                         conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03463                      }
03464                   }
03465                }
03466                if (user->warning_freq) {
03467                   nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
03468                } else {
03469                   nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
03470                }
03471             }
03472          }
03473 
03474          now = ast_tvnow();
03475          if (timeout && now.tv_sec >= timeout) {
03476             if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03477                ret = 0;
03478             } else {
03479                ret = -1;
03480             }
03481             break;
03482          }
03483 
03484          /* if we have just exited from the menu, and the user had a channel-driver
03485             volume adjustment, restore it
03486          */
03487          if (!menu_mode && menu_was_active && user->listen.desired && !user->listen.actual) {
03488             set_talk_volume(user, user->listen.desired);
03489          }
03490 
03491          menu_was_active = menu_mode;
03492 
03493          currentmarked = conf->markedusers;
03494          if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03495              ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
03496              ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
03497              lastmarked == 0) {
03498             if (currentmarked == 1 && conf->users > 1) {
03499                ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
03500                if (conf->users - 1 == 1) {
03501                   if (!ast_streamfile(chan, "conf-userwilljoin", chan->language)) {
03502                      ast_waitstream(chan, "");
03503                   }
03504                } else {
03505                   if (!ast_streamfile(chan, "conf-userswilljoin", chan->language)) {
03506                      ast_waitstream(chan, "");
03507                   }
03508                }
03509             }
03510             if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
03511                if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
03512                   ast_waitstream(chan, "");
03513                }
03514             }
03515          }
03516 
03517          /* Update the struct with the actual confflags */
03518          user->userflags = *confflags;
03519 
03520          if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
03521             if (currentmarked == 0) {
03522                if (lastmarked != 0) {
03523                   if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
03524                      if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language)) {
03525                         ast_waitstream(chan, "");
03526                      }
03527                   }
03528                   if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
03529                      if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03530                         ret = 0;
03531                      }
03532                      break;
03533                   } else {
03534                      dahdic.confmode = DAHDI_CONF_CONF;
03535                      if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03536                         ast_log(LOG_WARNING, "Error setting conference\n");
03537                         close(fd);
03538                         goto outrun;
03539                      }
03540                   }
03541                }
03542                if (!musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
03543                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03544                   musiconhold = 1;
03545                }
03546             } else if (currentmarked >= 1 && lastmarked == 0) {
03547                /* Marked user entered, so cancel timeout */
03548                timeout = 0;
03549                if (ast_test_flag64(confflags, CONFFLAG_MONITOR)) {
03550                   dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
03551                } else if (ast_test_flag64(confflags, CONFFLAG_TALKER)) {
03552                   dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
03553                } else {
03554                   dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
03555                }
03556                if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03557                   ast_log(LOG_WARNING, "Error setting conference\n");
03558                   close(fd);
03559                   goto outrun;
03560                }
03561                if (musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
03562                   ast_moh_stop(chan);
03563                   musiconhold = 0;
03564                }
03565                if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && 
03566                   !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
03567                   if (!ast_streamfile(chan, "conf-placeintoconf", chan->language)) {
03568                      ast_waitstream(chan, "");
03569                   }
03570                   conf_play(chan, conf, ENTER);
03571                }
03572             }
03573          }
03574 
03575          /* trying to add moh for single person conf */
03576          if (ast_test_flag64(confflags, CONFFLAG_MOH) && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
03577             if (conf->users == 1) {
03578                if (!musiconhold) {
03579                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03580                   musiconhold = 1;
03581                } 
03582             } else {
03583                if (musiconhold) {
03584                   ast_moh_stop(chan);
03585                   musiconhold = 0;
03586                }
03587             }
03588          }
03589          
03590          /* Leave if the last marked user left */
03591          if (currentmarked == 0 && lastmarked != 0 && ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
03592             if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03593                ret = 0;
03594             } else {
03595                ret = -1;
03596             }
03597             break;
03598          }
03599 
03600          /* Throw a TestEvent if a user exit did not cause this user to leave the conference */
03601          if (conf->users != lastusers) {
03602             if (conf->users < lastusers) {
03603                ast_test_suite_event_notify("NOEXIT", "Message: CONFFLAG_MARKEDEXIT\r\nLastUsers: %d\r\nUsers: %d", lastusers, conf->users);
03604             }
03605             lastusers = conf->users;
03606          }
03607 
03608          /* Check if my modes have changed */
03609 
03610          /* If I should be muted but am still talker, mute me */
03611          if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
03612             dahdic.confmode ^= DAHDI_CONF_TALKER;
03613             if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03614                ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
03615                ret = -1;
03616                break;
03617             }
03618 
03619             /* Indicate user is not talking anymore - change him to unmonitored state */
03620             if (ast_test_flag64(confflags,  (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER))) {
03621                set_user_talking(chan, conf, user, -1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03622             }
03623 
03624             ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
03625                "Channel: %s\r\n"
03626                "Uniqueid: %s\r\n"
03627                "Meetme: %s\r\n"
03628                "Usernum: %d\r\n"
03629                "Status: on\r\n",
03630                chan->name, chan->uniqueid, conf->confno, user->user_no);
03631          }
03632 
03633          /* If I should be un-muted but am not talker, un-mute me */
03634          if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
03635             dahdic.confmode |= DAHDI_CONF_TALKER;
03636             if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03637                ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
03638                ret = -1;
03639                break;
03640             }
03641 
03642             ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
03643                "Channel: %s\r\n"
03644                "Uniqueid: %s\r\n"
03645                "Meetme: %s\r\n"
03646                "Usernum: %d\r\n"
03647                "Status: off\r\n",
03648                chan->name, chan->uniqueid, conf->confno, user->user_no);
03649          }
03650          
03651          if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
03652             (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
03653             talkreq_manager = 1;
03654 
03655             ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
03656                "Channel: %s\r\n"
03657                "Uniqueid: %s\r\n"
03658                "Meetme: %s\r\n"
03659                "Usernum: %d\r\n"
03660                "Status: on\r\n",
03661                chan->name, chan->uniqueid, conf->confno, user->user_no);
03662          }
03663 
03664          
03665          if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
03666             !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
03667             talkreq_manager = 0;
03668             ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
03669                "Channel: %s\r\n"
03670                "Uniqueid: %s\r\n"
03671                "Meetme: %s\r\n"
03672                "Usernum: %d\r\n"
03673                "Status: off\r\n",
03674                chan->name, chan->uniqueid, conf->confno, user->user_no);
03675          }
03676          
03677          /* If I have been kicked, exit the conference */
03678          if (user->adminflags & ADMINFLAG_KICKME) {
03679             /* You have been kicked. */
03680             if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && 
03681                !ast_streamfile(chan, "conf-kicked", chan->language)) {
03682                ast_waitstream(chan, "");
03683             }
03684             ret = 0;
03685             break;
03686          }
03687 
03688          /* Perform a hangup check here since ast_waitfor_nandfds will not always be able to get a channel after a hangup has occurred */
03689          if (ast_check_hangup(chan)) {
03690             break;
03691          }
03692 
03693          c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
03694 
03695          if (c) {
03696             char dtmfstr[2] = "";
03697 
03698             if (c->fds[0] != origfd || (user->dahdichannel && (c->audiohooks || c->monitor))) {
03699                if (using_pseudo) {
03700                   /* Kill old pseudo */
03701                   close(fd);
03702                   using_pseudo = 0;
03703                }
03704                ast_debug(1, "Ooh, something swapped out under us, starting over\n");
03705                retrydahdi = (strcasecmp(c->tech->type, "DAHDI") || (c->audiohooks || c->monitor) ? 1 : 0);
03706                user->dahdichannel = !retrydahdi;
03707                goto dahdiretry;
03708             }
03709             if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
03710                f = ast_read_noaudio(c);
03711             } else {
03712                f = ast_read(c);
03713             }
03714             if (!f) {
03715                break;
03716             }
03717             if (f->frametype == AST_FRAME_DTMF) {
03718                dtmfstr[0] = f->subclass.integer;
03719                dtmfstr[1] = '\0';
03720             }
03721 
03722             if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.codec == AST_FORMAT_SLINEAR)) {
03723                if (user->talk.actual) {
03724                   ast_frame_adjust_volume(f, user->talk.actual);
03725                }
03726 
03727                if (ast_test_flag64(confflags, (CONFFLAG_OPTIMIZETALKER | CONFFLAG_MONITORTALKER))) {
03728                   if (user->talking == -1) {
03729                      user->talking = 0;
03730                   }
03731 
03732                   res = ast_dsp_silence(dsp, f, &totalsilence);
03733                   if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
03734                      set_user_talking(chan, conf, user, 1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03735                   }
03736 
03737                   if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
03738                      set_user_talking(chan, conf, user, 0, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03739                   }
03740                }
03741                if (using_pseudo) {
03742                   /* Absolutely do _not_ use careful_write here...
03743                      it is important that we read data from the channel
03744                      as fast as it arrives, and feed it into the conference.
03745                      The buffering in the pseudo channel will take care of any
03746                      timing differences, unless they are so drastic as to lose
03747                      audio frames (in which case carefully writing would only
03748                      have delayed the audio even further).
03749                   */
03750                   /* As it turns out, we do want to use careful write.  We just
03751                      don't want to block, but we do want to at least *try*
03752                      to write out all the samples.
03753                    */
03754                   if (user->talking || !ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER)) {
03755                      careful_write(fd, f->data.ptr, f->datalen, 0);
03756                   }
03757                }
03758             } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '*') && ast_test_flag64(confflags, CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_mode)) {
03759                if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03760                   conf_queue_dtmf(conf, user, f);
03761                }
03762                /* Take out of conference */
03763                if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
03764                   ast_log(LOG_WARNING, "Error setting conference\n");
03765                   close(fd);
03766                   ast_frfree(f);
03767                   goto outrun;
03768                }
03769 
03770                /* if we are entering the menu, and the user has a channel-driver
03771                   volume adjustment, clear it
03772                */
03773                if (!menu_mode && user->talk.desired && !user->talk.actual) {
03774                   set_talk_volume(user, 0);
03775                }
03776 
03777                if (musiconhold) {
03778                   ast_moh_stop(chan);
03779                } else if (!menu_mode) {
03780                   char *menu_to_play;
03781                   if (ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03782                      menu_mode = MENU_ADMIN;
03783                      menu_to_play = "conf-adminmenu-18";
03784                   } else {
03785                      menu_mode = MENU_NORMAL;
03786                      menu_to_play = "conf-usermenu-162";
03787                   }
03788 
03789                   if (!ast_streamfile(chan, menu_to_play, chan->language)) {
03790                      dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
03791                      ast_stopstream(chan);
03792                   } else {
03793                      dtmf = 0;
03794                   }
03795                } else {
03796                   dtmf = f->subclass.integer;
03797                }
03798 
03799                if (dtmf > 0) {
03800                   meetme_menu(&menu_mode, &dtmf, conf, confflags,
03801                      chan, user, recordingtmp, sizeof(recordingtmp));
03802                }
03803 
03804                if (musiconhold && !menu_mode) {
03805                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03806                }
03807 
03808                /* Put back into conference */
03809                if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03810                   ast_log(LOG_WARNING, "Error setting conference\n");
03811                   close(fd);
03812                   ast_frfree(f);
03813                   goto outrun;
03814                }
03815 
03816                conf_flush(fd, chan);
03817             /*
03818              * Since options using DTMF could absorb DTMF meant for the
03819              * conference menu, we have to check them after the menu.
03820              */
03821             } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
03822                if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03823                   conf_queue_dtmf(conf, user, f);
03824                }
03825 
03826                if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
03827                   ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
03828                   ret = 0;
03829                   ast_frfree(f);
03830                   break;
03831                } else {
03832                   ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
03833                }
03834             } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_KEYEXIT) &&
03835                (strchr(exitkeys, f->subclass.integer))) {
03836                pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
03837                   
03838                if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03839                   conf_queue_dtmf(conf, user, f);
03840                }
03841                ret = 0;
03842                ast_frfree(f);
03843                break;
03844             } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
03845                && ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03846                conf_queue_dtmf(conf, user, f);
03847             } else if (ast_test_flag64(confflags, CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
03848                switch (f->subclass.integer) {
03849                case AST_CONTROL_HOLD:
03850                   sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
03851                   break;
03852                default:
03853                   break;
03854                }
03855             } else if (f->frametype == AST_FRAME_NULL) {
03856                /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
03857             } else if (f->frametype == AST_FRAME_CONTROL) {
03858                switch (f->subclass.integer) {
03859                case AST_CONTROL_BUSY:
03860                case AST_CONTROL_CONGESTION:
03861                   ast_frfree(f);
03862                   goto outrun;
03863                   break;
03864                default:
03865                   ast_debug(1, 
03866                      "Got ignored control frame on channel %s, f->frametype=%u,f->subclass=%d\n",
03867                      chan->name, f->frametype, f->subclass.integer);
03868                }
03869             } else {
03870                ast_debug(1, 
03871                   "Got unrecognized frame on channel %s, f->frametype=%u,f->subclass=%d\n",
03872                   chan->name, f->frametype, f->subclass.integer);
03873             }
03874             ast_frfree(f);
03875          } else if (outfd > -1) {
03876             res = read(outfd, buf, CONF_SIZE);
03877             if (res > 0) {
03878                memset(&fr, 0, sizeof(fr));
03879                fr.frametype = AST_FRAME_VOICE;
03880                fr.subclass.codec = AST_FORMAT_SLINEAR;
03881                fr.datalen = res;
03882                fr.samples = res / 2;
03883                fr.data.ptr = buf;
03884                fr.offset = AST_FRIENDLY_OFFSET;
03885                if (!user->listen.actual &&
03886                   (ast_test_flag64(confflags, CONFFLAG_MONITOR) ||
03887                    (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
03888                    (!user->talking && ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER))
03889                    )) {
03890                   int idx;
03891                   for (idx = 0; idx < AST_FRAME_BITS; idx++) {
03892                      if (chan->rawwriteformat & (1 << idx)) {
03893                         break;
03894                      }
03895                   }
03896                   if (idx >= AST_FRAME_BITS) {
03897                      goto bailoutandtrynormal;
03898                   }
03899                   ast_mutex_lock(&conf->listenlock);
03900                   if (!conf->transframe[idx]) {
03901                      if (conf->origframe) {
03902                         if (musiconhold
03903                            && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)
03904                            && !ast_dsp_silence(dsp, conf->origframe, &confsilence)
03905                            && confsilence < MEETME_DELAYDETECTTALK) {
03906                            ast_moh_stop(chan);
03907                            mohtempstopped = 1;
03908                         }
03909                         if (!conf->transpath[idx]) {
03910                            conf->transpath[idx] = ast_translator_build_path((1 << idx), AST_FORMAT_SLINEAR);
03911                         }
03912                         if (conf->transpath[idx]) {
03913                            conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
03914                            if (!conf->transframe[idx]) {
03915                               conf->transframe[idx] = &ast_null_frame;
03916                            }
03917                         }
03918                      }
03919                   }
03920                   if (conf->transframe[idx]) {
03921                      if ((conf->transframe[idx]->frametype != AST_FRAME_NULL) &&
03922                          can_write(chan, confflags)) {
03923                         struct ast_frame *cur;
03924                         /* the translator may have returned a list of frames, so
03925                            write each one onto the channel
03926                         */
03927                         for (cur = conf->transframe[idx]; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
03928                            if (ast_write(chan, cur)) {
03929                               ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
03930                               break;
03931                            }
03932                         }
03933                         if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
03934                            mohtempstopped = 0;
03935                            conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03936                         }
03937                      }
03938                   } else {
03939                      ast_mutex_unlock(&conf->listenlock);
03940                      goto bailoutandtrynormal;
03941                   }
03942                   ast_mutex_unlock(&conf->listenlock);
03943                } else {
03944 bailoutandtrynormal:
03945                   if (musiconhold
03946                      && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)
03947                      && !ast_dsp_silence(dsp, &fr, &confsilence)
03948                      && confsilence < MEETME_DELAYDETECTTALK) {
03949                      ast_moh_stop(chan);
03950                      mohtempstopped = 1;
03951                   }
03952                   if (user->listen.actual) {
03953                      ast_frame_adjust_volume(&fr, user->listen.actual);
03954                   }
03955                   if (can_write(chan, confflags) && ast_write(chan, &fr) < 0) {
03956                      ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
03957                   }
03958                   if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
03959                      mohtempstopped = 0;
03960                      conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03961                   }
03962                }
03963             } else {
03964                ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
03965             }
03966          }
03967          lastmarked = currentmarked;
03968       }
03969    }
03970 
03971    if (musiconhold) {
03972       ast_moh_stop(chan);
03973    }
03974    
03975    if (using_pseudo) {
03976       close(fd);
03977    } else {
03978       /* Take out of conference */
03979       dahdic.chan = 0;  
03980       dahdic.confno = 0;
03981       dahdic.confmode = 0;
03982       if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03983          ast_log(LOG_WARNING, "Error setting conference\n");
03984       }
03985    }
03986 
03987    reset_volumes(user);
03988 
03989    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
03990       !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03991       conf_play(chan, conf, LEAVE);
03992    }
03993 
03994    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) || ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
03995       struct announce_listitem *item;
03996       if (!(item = ao2_alloc(sizeof(*item), NULL)))
03997          goto outrun;
03998       ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
03999       ast_copy_string(item->language, chan->language, sizeof(item->language));
04000       item->confchan = conf->chan;
04001       item->confusers = conf->users;
04002       item->announcetype = CONF_HASLEFT;
04003       ast_mutex_lock(&conf->announcelistlock);
04004       AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
04005       ast_cond_signal(&conf->announcelist_addition);
04006       ast_mutex_unlock(&conf->announcelistlock);
04007    } else if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
04008       ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW)) && conf->users == 1) {
04009       /* Last person is leaving, so no reason to try and announce, but should delete the name recording */
04010       ast_filedelete(user->namerecloc, NULL);
04011    }
04012 
04013  outrun:
04014    AST_LIST_LOCK(&confs);
04015 
04016    if (dsp) {
04017       ast_dsp_free(dsp);
04018    }
04019    
04020    if (user->user_no) {
04021       /* Only cleanup users who really joined! */
04022       now = ast_tvnow();
04023 
04024       if (sent_event) {
04025          ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeLeave",
04026             "Channel: %s\r\n"
04027             "Uniqueid: %s\r\n"
04028             "Meetme: %s\r\n"
04029             "Usernum: %d\r\n"
04030             "CallerIDNum: %s\r\n"
04031             "CallerIDName: %s\r\n"
04032             "ConnectedLineNum: %s\r\n"
04033             "ConnectedLineName: %s\r\n"
04034             "Duration: %ld\r\n",
04035             chan->name, chan->uniqueid, conf->confno,
04036             user->user_no,
04037             S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
04038             S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<unknown>"),
04039             S_COR(user->chan->connected.id.number.valid, user->chan->connected.id.number.str, "<unknown>"),
04040             S_COR(user->chan->connected.id.name.valid, user->chan->connected.id.name.str, "<unknown>"),
04041             (long)(now.tv_sec - user->jointime));
04042       }
04043 
04044       if (setusercount) {
04045          conf->users--;
04046          if (rt_log_members) {
04047             /* Update table */
04048             snprintf(members, sizeof(members), "%d", conf->users);
04049             ast_realtime_require_field("meetme",
04050                "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
04051                "members", RQ_UINTEGER1, strlen(members),
04052                NULL);
04053             ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
04054          }
04055          if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
04056             conf->markedusers--;
04057          }
04058       }
04059       /* Remove ourselves from the container */
04060       ao2_unlink(conf->usercontainer, user); 
04061 
04062       /* Change any states */
04063       if (!conf->users) {
04064          ast_devstate_changed(AST_DEVICE_NOT_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
04065       }
04066 
04067       /* Return the number of seconds the user was in the conf */
04068       snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
04069       pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
04070 
04071       /* Return the RealTime bookid for CDR linking */
04072       if (rt_schedule) {
04073          pbx_builtin_setvar_helper(chan, "MEETMEBOOKID", conf->bookid);
04074       }
04075    }
04076    ao2_ref(user, -1);
04077    AST_LIST_UNLOCK(&confs);
04078 
04079    return ret;
04080 }
04081 
04082 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
04083             char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags, int *too_early, char **optargs)
04084 {
04085    struct ast_variable *var, *origvar;
04086    struct ast_conference *cnf;
04087 
04088    *too_early = 0;
04089 
04090    /* Check first in the conference list */
04091    AST_LIST_LOCK(&confs);
04092    AST_LIST_TRAVERSE(&confs, cnf, list) {
04093       if (!strcmp(confno, cnf->confno)) {
04094          break;
04095       }
04096    }
04097    if (cnf) {
04098       cnf->refcount += refcount;
04099    }
04100    AST_LIST_UNLOCK(&confs);
04101 
04102    if (!cnf) {
04103       char *pin = NULL, *pinadmin = NULL; /* For temp use */
04104       int maxusers = 0;
04105       struct timeval now;
04106       char recordingfilename[256] = "";
04107       char recordingformat[11] = "";
04108       char currenttime[32] = "";
04109       char eatime[32] = "";
04110       char bookid[51] = "";
04111       char recordingtmp[AST_MAX_EXTENSION] = "";
04112       char useropts[OPTIONS_LEN + 1] = ""; /* Used for RealTime conferences */
04113       char adminopts[OPTIONS_LEN + 1] = "";
04114       struct ast_tm tm, etm;
04115       struct timeval endtime = { .tv_sec = 0 };
04116       const char *var2;
04117 
04118       if (rt_schedule) {
04119          now = ast_tvnow();
04120 
04121          ast_localtime(&now, &tm, NULL);
04122          ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
04123 
04124          ast_debug(1, "Looking for conference %s that starts after %s\n", confno, currenttime);
04125 
04126          var = ast_load_realtime("meetme", "confno",
04127             confno, "starttime <= ", currenttime, "endtime >= ",
04128             currenttime, NULL);
04129 
04130          if (!var && fuzzystart) {
04131             now = ast_tvnow();
04132             now.tv_sec += fuzzystart;
04133 
04134             ast_localtime(&now, &tm, NULL);
04135             ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
04136             var = ast_load_realtime("meetme", "confno",
04137                confno, "starttime <= ", currenttime, "endtime >= ",
04138                currenttime, NULL);
04139          }
04140 
04141          if (!var && earlyalert) {
04142             now = ast_tvnow();
04143             now.tv_sec += earlyalert;
04144             ast_localtime(&now, &etm, NULL);
04145             ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
04146             var = ast_load_realtime("meetme", "confno",
04147                confno, "starttime <= ", eatime, "endtime >= ",
04148                currenttime, NULL);
04149             if (var) {
04150                *too_early = 1;
04151             }
04152          }
04153 
04154       } else {
04155           var = ast_load_realtime("meetme", "confno", confno, NULL);
04156       }
04157 
04158       if (!var) {
04159          return NULL;
04160       }
04161 
04162       if (rt_schedule && *too_early) {
04163          /* Announce that the caller is early and exit */
04164          if (!ast_streamfile(chan, "conf-has-not-started", chan->language)) {
04165             ast_waitstream(chan, "");
04166          }
04167          ast_variables_destroy(var);
04168          return NULL;
04169       }
04170 
04171       for (origvar = var; var; var = var->next) {
04172          if (!strcasecmp(var->name, "pin")) {
04173             pin = ast_strdupa(var->value);
04174          } else if (!strcasecmp(var->name, "adminpin")) {
04175             pinadmin = ast_strdupa(var->value);
04176          } else if (!strcasecmp(var->name, "bookId")) {
04177             ast_copy_string(bookid, var->value, sizeof(bookid));
04178          } else if (!strcasecmp(var->name, "opts")) {
04179             ast_copy_string(useropts, var->value, sizeof(char[OPTIONS_LEN + 1]));
04180          } else if (!strcasecmp(var->name, "maxusers")) {
04181             maxusers = atoi(var->value);
04182          } else if (!strcasecmp(var->name, "adminopts")) {
04183             ast_copy_string(adminopts, var->value, sizeof(char[OPTIONS_LEN + 1]));
04184          } else if (!strcasecmp(var->name, "recordingfilename")) {
04185             ast_copy_string(recordingfilename, var->value, sizeof(recordingfilename));
04186          } else if (!strcasecmp(var->name, "recordingformat")) {
04187             ast_copy_string(recordingformat, var->value, sizeof(recordingformat));
04188          } else if (!strcasecmp(var->name, "endtime")) {
04189             struct ast_tm endtime_tm;
04190             ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
04191             endtime = ast_mktime(&endtime_tm, NULL);
04192          }
04193       }
04194 
04195       ast_variables_destroy(origvar);
04196 
04197       cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan, NULL);
04198 
04199       if (cnf) {
04200          struct ast_flags64 tmp_flags;
04201 
04202          cnf->maxusers = maxusers;
04203          cnf->endalert = endalert;
04204          cnf->endtime = endtime.tv_sec;
04205          cnf->useropts = ast_strdup(useropts);
04206          cnf->adminopts = ast_strdup(adminopts);
04207          cnf->bookid = ast_strdup(bookid);
04208          if (!ast_strlen_zero(recordingfilename)) {
04209             cnf->recordingfilename = ast_strdup(recordingfilename);
04210          }
04211          if (!ast_strlen_zero(recordingformat)) {
04212             cnf->recordingformat = ast_strdup(recordingformat);
04213          }
04214 
04215          /* Parse the other options into confflags -- need to do this in two
04216           * steps, because the parse_options routine zeroes the buffer. */
04217          ast_app_parse_options64(meetme_opts, &tmp_flags, optargs, useropts);
04218          ast_copy_flags64(confflags, &tmp_flags, tmp_flags.flags);
04219 
04220          if (strchr(cnf->useropts, 'r')) {
04221             if (ast_strlen_zero(recordingfilename)) { /* If the recordingfilename in the database is empty, use the channel definition or use the default. */
04222                ast_channel_lock(chan);
04223                if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
04224                   ast_free(cnf->recordingfilename);
04225                   cnf->recordingfilename = ast_strdup(var2);
04226                }
04227                ast_channel_unlock(chan);
04228                if (ast_strlen_zero(cnf->recordingfilename)) {
04229                   snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", cnf->confno, chan->uniqueid);
04230                   ast_free(cnf->recordingfilename);
04231                   cnf->recordingfilename = ast_strdup(recordingtmp);
04232                }
04233             }
04234             if (ast_strlen_zero(cnf->recordingformat)) {/* If the recording format is empty, use the wav as default */
04235                ast_channel_lock(chan);
04236                if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
04237                   ast_free(cnf->recordingformat);
04238                   cnf->recordingformat = ast_strdup(var2);
04239                }
04240                ast_channel_unlock(chan);
04241                if (ast_strlen_zero(cnf->recordingformat)) {
04242                   ast_free(cnf->recordingformat);
04243                   cnf->recordingformat = ast_strdup("wav");
04244                }
04245             }
04246             ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
04247          }
04248       }
04249    }
04250 
04251    if (cnf) {
04252       if (confflags->flags && !cnf->chan &&
04253           !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
04254           ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW)) {
04255          ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
04256          ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW);
04257       }
04258 
04259       if (confflags && !cnf->chan &&
04260           ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
04261          ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
04262          ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
04263       }
04264    }
04265 
04266    return cnf;
04267 }
04268 
04269 
04270 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
04271                char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags)
04272 {
04273    struct ast_config *cfg;
04274    struct ast_variable *var;
04275    struct ast_flags config_flags = { 0 };
04276    struct ast_conference *cnf;
04277 
04278    AST_DECLARE_APP_ARGS(args,
04279       AST_APP_ARG(confno);
04280       AST_APP_ARG(pin);
04281       AST_APP_ARG(pinadmin);
04282    );
04283 
04284    /* Check first in the conference list */
04285    ast_debug(1, "The requested confno is '%s'?\n", confno);
04286    AST_LIST_LOCK(&confs);
04287    AST_LIST_TRAVERSE(&confs, cnf, list) {
04288       ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
04289       if (!strcmp(confno, cnf->confno)) 
04290          break;
04291    }
04292    if (cnf) {
04293       cnf->refcount += refcount;
04294    }
04295    AST_LIST_UNLOCK(&confs);
04296 
04297    if (!cnf) {
04298       if (dynamic) {
04299          /* No need to parse meetme.conf */
04300          ast_debug(1, "Building dynamic conference '%s'\n", confno);
04301          if (dynamic_pin) {
04302             if (dynamic_pin[0] == 'q') {
04303                /* Query the user to enter a PIN */
04304                if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
04305                   return NULL;
04306             }
04307             cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan, NULL);
04308          } else {
04309             cnf = build_conf(confno, "", "", make, dynamic, refcount, chan, NULL);
04310          }
04311       } else {
04312          /* Check the config */
04313          cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04314          if (!cfg) {
04315             ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
04316             return NULL;
04317          } else if (cfg == CONFIG_STATUS_FILEINVALID) {
04318             ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format.  Aborting.\n");
04319             return NULL;
04320          }
04321 
04322          for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
04323             char parse[MAX_SETTINGS];
04324 
04325             if (strcasecmp(var->name, "conf"))
04326                continue;
04327 
04328             ast_copy_string(parse, var->value, sizeof(parse));
04329 
04330             AST_STANDARD_APP_ARGS(args, parse);
04331             ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
04332             if (!strcasecmp(args.confno, confno)) {
04333                /* Bingo it's a valid conference */
04334                cnf = build_conf(args.confno,
04335                      S_OR(args.pin, ""),
04336                      S_OR(args.pinadmin, ""),
04337                      make, dynamic, refcount, chan, NULL);
04338                break;
04339             }
04340          }
04341          if (!var) {
04342             ast_debug(1, "%s isn't a valid conference\n", confno);
04343          }
04344          ast_config_destroy(cfg);
04345       }
04346    } else if (dynamic_pin) {
04347       /* Correct for the user selecting 'D' instead of 'd' to have
04348          someone join into a conference that has already been created
04349          with a pin. */
04350       if (dynamic_pin[0] == 'q') {
04351          dynamic_pin[0] = '\0';
04352       }
04353    }
04354 
04355    if (cnf) {
04356       if (confflags && !cnf->chan &&
04357           !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
04358           ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW)) {
04359          ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
04360          ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW);
04361       }
04362       
04363       if (confflags && !cnf->chan &&
04364           ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
04365          ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
04366          ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
04367       }
04368    }
04369 
04370    return cnf;
04371 }
04372 
04373 /*! \brief The MeetmeCount application */
04374 static int count_exec(struct ast_channel *chan, const char *data)
04375 {
04376    int res = 0;
04377    struct ast_conference *conf;
04378    int count;
04379    char *localdata;
04380    char val[80] = "0"; 
04381    AST_DECLARE_APP_ARGS(args,
04382       AST_APP_ARG(confno);
04383       AST_APP_ARG(varname);
04384    );
04385 
04386    if (ast_strlen_zero(data)) {
04387       ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
04388       return -1;
04389    }
04390    
04391    localdata = ast_strdupa(data);
04392 
04393    AST_STANDARD_APP_ARGS(args, localdata);
04394    
04395    conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
04396 
04397    if (conf) {
04398       count = conf->users;
04399       dispose_conf(conf);
04400       conf = NULL;
04401    } else
04402       count = 0;
04403 
04404    if (!ast_strlen_zero(args.varname)) {
04405       /* have var so load it and exit */
04406       snprintf(val, sizeof(val), "%d", count);
04407       pbx_builtin_setvar_helper(chan, args.varname, val);
04408    } else {
04409       if (chan->_state != AST_STATE_UP) {
04410          ast_answer(chan);
04411       }
04412       res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
04413    }
04414 
04415    return res;
04416 }
04417 
04418 /*! \brief The meetme() application */
04419 static int conf_exec(struct ast_channel *chan, const char *data)
04420 {
04421    int res = -1;
04422    char confno[MAX_CONFNUM] = "";
04423    int allowretry = 0;
04424    int retrycnt = 0;
04425    struct ast_conference *cnf = NULL;
04426    struct ast_flags64 confflags = {0};
04427    struct ast_flags config_flags = { 0 };
04428    int dynamic = 0;
04429    int empty = 0, empty_no_pin = 0;
04430    int always_prompt = 0;
04431    const char *notdata;
04432    char *info, the_pin[MAX_PIN] = "";
04433    AST_DECLARE_APP_ARGS(args,
04434       AST_APP_ARG(confno);
04435       AST_APP_ARG(options);
04436       AST_APP_ARG(pin);
04437    );
04438    char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
04439 
04440    if (ast_strlen_zero(data)) {
04441       allowretry = 1;
04442       notdata = "";
04443    } else {
04444       notdata = data;
04445    }
04446    
04447    if (chan->_state != AST_STATE_UP)
04448       ast_answer(chan);
04449 
04450    info = ast_strdupa(notdata);
04451 
04452    AST_STANDARD_APP_ARGS(args, info);  
04453 
04454    if (args.confno) {
04455       ast_copy_string(confno, args.confno, sizeof(confno));
04456       if (ast_strlen_zero(confno)) {
04457          allowretry = 1;
04458       }
04459    }
04460    
04461    if (args.pin)
04462       ast_copy_string(the_pin, args.pin, sizeof(the_pin));
04463 
04464    if (args.options) {
04465       ast_app_parse_options64(meetme_opts, &confflags, optargs, args.options);
04466       dynamic = ast_test_flag64(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
04467       if (ast_test_flag64(&confflags, CONFFLAG_DYNAMICPIN) && ast_strlen_zero(args.pin))
04468          strcpy(the_pin, "q");
04469 
04470       empty = ast_test_flag64(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
04471       empty_no_pin = ast_test_flag64(&confflags, CONFFLAG_EMPTYNOPIN);
04472       always_prompt = ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT | CONFFLAG_DYNAMICPIN);
04473    }
04474 
04475    do {
04476       if (retrycnt > 3)
04477          allowretry = 0;
04478       if (empty) {
04479          int i;
04480          struct ast_config *cfg;
04481          struct ast_variable *var;
04482          int confno_int;
04483 
04484          /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
04485          if ((empty_no_pin) || (!dynamic)) {
04486             cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04487             if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
04488                var = ast_variable_browse(cfg, "rooms");
04489                while (var) {
04490                   char parse[MAX_SETTINGS], *stringp = parse, *confno_tmp;
04491                   if (!strcasecmp(var->name, "conf")) {
04492                      int found = 0;
04493                      ast_copy_string(parse, var->value, sizeof(parse));
04494                      confno_tmp = strsep(&stringp, "|,");
04495                      if (!dynamic) {
04496                         /* For static:  run through the list and see if this conference is empty */
04497                         AST_LIST_LOCK(&confs);
04498                         AST_LIST_TRAVERSE(&confs, cnf, list) {
04499                            if (!strcmp(confno_tmp, cnf->confno)) {
04500                               /* The conference exists, therefore it's not empty */
04501                               found = 1;
04502                               break;
04503                            }
04504                         }
04505                         AST_LIST_UNLOCK(&confs);
04506                         cnf = NULL;
04507                         if (!found) {
04508                            /* At this point, we have a confno_tmp (static conference) that is empty */
04509                            if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
04510                               /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
04511                                * Case 2:  empty_no_pin and pin is blank (but not NULL)
04512                                * Case 3:  not empty_no_pin
04513                                */
04514                               ast_copy_string(confno, confno_tmp, sizeof(confno));
04515                               break;
04516                            }
04517                         }
04518                      }
04519                   }
04520                   var = var->next;
04521                }
04522                ast_config_destroy(cfg);
04523             }
04524 
04525             if (ast_strlen_zero(confno) && (cfg = ast_load_realtime_multientry("meetme", "confno LIKE", "%", SENTINEL))) {
04526                const char *catg;
04527                for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
04528                   const char *confno_tmp = ast_variable_retrieve(cfg, catg, "confno");
04529                   const char *pin_tmp = ast_variable_retrieve(cfg, catg, "pin");
04530                   if (ast_strlen_zero(confno_tmp)) {
04531                      continue;
04532                   }
04533                   if (!dynamic) {
04534                      int found = 0;
04535                      /* For static:  run through the list and see if this conference is empty */
04536                      AST_LIST_LOCK(&confs);
04537                      AST_LIST_TRAVERSE(&confs, cnf, list) {
04538                         if (!strcmp(confno_tmp, cnf->confno)) {
04539                            /* The conference exists, therefore it's not empty */
04540                            found = 1;
04541                            break;
04542                         }
04543                      }
04544                      AST_LIST_UNLOCK(&confs);
04545                      if (!found) {
04546                         /* At this point, we have a confno_tmp (realtime conference) that is empty */
04547                         if ((empty_no_pin && ast_strlen_zero(pin_tmp)) || (!empty_no_pin)) {
04548                            /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
04549                             * Case 2:  empty_no_pin and pin is blank (but not NULL)
04550                             * Case 3:  not empty_no_pin
04551                             */
04552                            ast_copy_string(confno, confno_tmp, sizeof(confno));
04553                            break;
04554                         }
04555                      }
04556                   }
04557                }
04558                ast_config_destroy(cfg);
04559             }
04560          }
04561 
04562          /* Select first conference number not in use */
04563          if (ast_strlen_zero(confno) && dynamic) {
04564             AST_LIST_LOCK(&confs);
04565             for (i = 0; i < ARRAY_LEN(conf_map); i++) {
04566                if (!conf_map[i]) {
04567                   snprintf(confno, sizeof(confno), "%d", i);
04568                   conf_map[i] = 1;
04569                   break;
04570                }
04571             }
04572             AST_LIST_UNLOCK(&confs);
04573          }
04574 
04575          /* Not found? */
04576          if (ast_strlen_zero(confno)) {
04577             ast_test_suite_event_notify("PLAYBACK", "Message: conf-noempty");
04578             res = ast_streamfile(chan, "conf-noempty", chan->language);
04579             if (!res)
04580                ast_waitstream(chan, "");
04581          } else {
04582             if (sscanf(confno, "%30d", &confno_int) == 1) {
04583                if (!ast_test_flag64(&confflags, CONFFLAG_QUIET)) {
04584                   res = ast_streamfile(chan, "conf-enteringno", chan->language);
04585                   if (!res) {
04586                      ast_waitstream(chan, "");
04587                      res = ast_say_digits(chan, confno_int, "", chan->language);
04588                   }
04589                }
04590             } else {
04591                ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
04592             }
04593          }
04594       }
04595 
04596       while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
04597          /* Prompt user for conference number */
04598          res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
04599          if (res < 0) {
04600             /* Don't try to validate when we catch an error */
04601             confno[0] = '\0';
04602             allowretry = 0;
04603             break;
04604          }
04605       }
04606       if (!ast_strlen_zero(confno)) {
04607          /* Check the validity of the conference */
04608          cnf = find_conf(chan, confno, 1, dynamic, the_pin, 
04609             sizeof(the_pin), 1, &confflags);
04610          if (!cnf) {
04611             int too_early = 0;
04612 
04613             cnf = find_conf_realtime(chan, confno, 1, dynamic, 
04614                the_pin, sizeof(the_pin), 1, &confflags, &too_early, optargs);
04615             if (rt_schedule && too_early)
04616                allowretry = 0;
04617          }
04618 
04619          if (!cnf) {
04620             if (allowretry) {
04621                confno[0] = '\0';
04622                res = ast_streamfile(chan, "conf-invalid", chan->language);
04623                if (!res)
04624                   ast_waitstream(chan, "");
04625                res = -1;
04626             }
04627          } else {
04628             /* Conference requires a pin for specified access level */
04629             int req_pin = !ast_strlen_zero(cnf->pin) ||
04630                (!ast_strlen_zero(cnf->pinadmin) &&
04631                   ast_test_flag64(&confflags, CONFFLAG_ADMIN));
04632             /* The following logic was derived from a
04633              * 4 variable truth table and defines which
04634              * circumstances are not exempt from pin
04635              * checking.
04636              * If this needs to be modified, write the
04637              * truth table back out from the boolean
04638              * expression AB+A'D+C', change the erroneous
04639              * result, and rederive the expression.
04640              * Variables:
04641              *  A: pin provided?
04642              *  B: always prompt?
04643              *  C: dynamic?
04644              *  D: has users? */
04645             int not_exempt = !cnf->isdynamic;
04646             not_exempt = not_exempt || (!ast_strlen_zero(args.pin) && ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT));
04647             not_exempt = not_exempt || (ast_strlen_zero(args.pin) && cnf->users);
04648             if (req_pin && not_exempt) {
04649                char pin[MAX_PIN] = "";
04650                int j;
04651 
04652                /* Allow the pin to be retried up to 3 times */
04653                for (j = 0; j < 3; j++) {
04654                   if (*the_pin && (always_prompt == 0)) {
04655                      ast_copy_string(pin, the_pin, sizeof(pin));
04656                      res = 0;
04657                   } else {
04658                      /* Prompt user for pin if pin is required */
04659                      ast_test_suite_event_notify("PLAYBACK", "Message: conf-getpin\r\n"
04660                         "Channel: %s",
04661                         chan->name);
04662                      res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
04663                   }
04664                   if (res >= 0) {
04665                      if ((!strcasecmp(pin, cnf->pin) &&
04666                           (ast_strlen_zero(cnf->pinadmin) ||
04667                            !ast_test_flag64(&confflags, CONFFLAG_ADMIN))) ||
04668                           (!ast_strlen_zero(cnf->pinadmin) &&
04669                            !strcasecmp(pin, cnf->pinadmin))) {
04670                         /* Pin correct */
04671                         allowretry = 0;
04672                         if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) {
04673                            if (!ast_strlen_zero(cnf->adminopts)) {
04674                               char *opts = ast_strdupa(cnf->adminopts);
04675                               ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04676                            }
04677                         } else {
04678                            if (!ast_strlen_zero(cnf->useropts)) {
04679                               char *opts = ast_strdupa(cnf->useropts);
04680                               ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04681                            }
04682                         }
04683                         /* Run the conference */
04684                         ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
04685                         res = conf_run(chan, cnf, &confflags, optargs);
04686                         break;
04687                      } else {
04688                         /* Pin invalid */
04689                         if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
04690                            res = ast_waitstream(chan, AST_DIGIT_ANY);
04691                            ast_stopstream(chan);
04692                         } else {
04693                            ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
04694                            break;
04695                         }
04696                         if (res < 0)
04697                            break;
04698                         pin[0] = res;
04699                         pin[1] = '\0';
04700                         res = -1;
04701                         if (allowretry)
04702                            confno[0] = '\0';
04703                      }
04704                   } else {
04705                      /* failed when getting the pin */
04706                      res = -1;
04707                      allowretry = 0;
04708                      /* see if we need to get rid of the conference */
04709                      break;
04710                   }
04711 
04712                   /* Don't retry pin with a static pin */
04713                   if (*the_pin && (always_prompt == 0)) {
04714                      break;
04715                   }
04716                }
04717             } else {
04718                /* No pin required */
04719                allowretry = 0;
04720 
04721                /* For RealTime conferences without a pin 
04722                 * should still support loading options
04723                 */
04724                if (!ast_strlen_zero(cnf->useropts)) {
04725                   char *opts = ast_strdupa(cnf->useropts);
04726                   ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04727                }
04728 
04729                /* Run the conference */
04730                res = conf_run(chan, cnf, &confflags, optargs);
04731             }
04732             dispose_conf(cnf);
04733             cnf = NULL;
04734          }
04735       }
04736    } while (allowretry);
04737 
04738    if (cnf)
04739       dispose_conf(cnf);
04740    
04741    return res;
04742 }
04743 
04744 static struct ast_conf_user *find_user(struct ast_conference *conf, const char *callerident)
04745 {
04746    struct ast_conf_user *user = NULL;
04747    int cid;
04748 
04749    if (conf && callerident && sscanf(callerident, "%30d", &cid) == 1) {
04750       user = ao2_find(conf->usercontainer, &cid, 0);
04751       /* reference decremented later in admin_exec */
04752       return user;
04753    }
04754    return NULL;
04755 }
04756 
04757 static int user_listen_volup_cb(void *obj, void *unused, int flags)
04758 {
04759    struct ast_conf_user *user = obj;
04760    tweak_listen_volume(user, VOL_UP);
04761    return 0;
04762 }
04763 
04764 static int user_listen_voldown_cb(void *obj, void *unused, int flags)
04765 {
04766    struct ast_conf_user *user = obj;
04767    tweak_listen_volume(user, VOL_DOWN);
04768    return 0;
04769 }
04770 
04771 static int user_talk_volup_cb(void *obj, void *unused, int flags)
04772 {
04773    struct ast_conf_user *user = obj;
04774    tweak_talk_volume(user, VOL_UP);
04775    return 0;
04776 }
04777 
04778 static int user_talk_voldown_cb(void *obj, void *unused, int flags)
04779 {
04780    struct ast_conf_user *user = obj;
04781    tweak_talk_volume(user, VOL_DOWN);
04782    return 0;
04783 }
04784 
04785 static int user_reset_vol_cb(void *obj, void *unused, int flags)
04786 {
04787    struct ast_conf_user *user = obj;
04788    reset_volumes(user);
04789    return 0;
04790 }
04791 
04792 static int user_chan_cb(void *obj, void *args, int flags)
04793 {
04794    struct ast_conf_user *user = obj;
04795    const char *channel = args;
04796 
04797    if (!strcmp(user->chan->name, channel)) {
04798       return (CMP_MATCH | CMP_STOP);
04799    }
04800 
04801    return 0;
04802 }
04803 
04804 /*! \brief The MeetMeadmin application 
04805 
04806   MeetMeAdmin(confno, command, caller) */
04807 static int admin_exec(struct ast_channel *chan, const char *data) {
04808    char *params;
04809    struct ast_conference *cnf;
04810    struct ast_conf_user *user = NULL;
04811    AST_DECLARE_APP_ARGS(args,
04812       AST_APP_ARG(confno);
04813       AST_APP_ARG(command);
04814       AST_APP_ARG(user);
04815    );
04816    int res = 0;
04817 
04818    if (ast_strlen_zero(data)) {
04819       ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
04820       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
04821       return -1;
04822    }
04823 
04824    params = ast_strdupa(data);
04825    AST_STANDARD_APP_ARGS(args, params);
04826 
04827    if (!args.command) {
04828       ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
04829       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
04830       return -1;
04831    }
04832 
04833    AST_LIST_LOCK(&confs);
04834    AST_LIST_TRAVERSE(&confs, cnf, list) {
04835       if (!strcmp(cnf->confno, args.confno))
04836          break;
04837    }
04838 
04839    if (!cnf) {
04840       ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
04841       AST_LIST_UNLOCK(&confs);
04842       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOTFOUND");
04843       return 0;
04844    }
04845 
04846    ast_atomic_fetchadd_int(&cnf->refcount, 1);
04847 
04848    if (args.user) {
04849       user = find_user(cnf, args.user);
04850       if (!user) {
04851          ast_log(LOG_NOTICE, "Specified User not found!\n");
04852          res = -2;
04853          goto usernotfound;
04854       }
04855    } else {
04856       /* fail for commands that require a user */
04857       switch (*args.command) {
04858       case 'm': /* Unmute */
04859       case 'M': /* Mute */
04860       case 't': /* Lower user's talk volume */
04861       case 'T': /* Raise user's talk volume */
04862       case 'u': /* Lower user's listen volume */
04863       case 'U': /* Raise user's listen volume */
04864       case 'r': /* Reset user's volume level */
04865       case 'k': /* Kick user */
04866          res = -2;
04867          ast_log(LOG_NOTICE, "No user specified!\n");
04868          goto usernotfound;
04869       default:
04870          break;
04871       }
04872    }
04873 
04874    switch (*args.command) {
04875    case 76: /* L: Lock */ 
04876       cnf->locked = 1;
04877       break;
04878    case 108: /* l: Unlock */ 
04879       cnf->locked = 0;
04880       break;
04881    case 75: /* K: kick all users */
04882       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_kickme_cb, NULL);
04883       break;
04884    case 101: /* e: Eject last user*/
04885    {
04886       int max_no = 0;
04887       struct ast_conf_user *eject_user;
04888 
04889       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
04890       eject_user = ao2_find(cnf->usercontainer, &max_no, 0);
04891       if (!eject_user) {
04892          res = -1;
04893          ast_log(LOG_NOTICE, "No last user to kick!\n");
04894          break;
04895       }
04896 
04897       if (!ast_test_flag64(&eject_user->userflags, CONFFLAG_ADMIN)) {
04898          eject_user->adminflags |= ADMINFLAG_KICKME;
04899       } else {
04900          res = -1;
04901          ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
04902       }
04903 
04904       ao2_ref(eject_user, -1);
04905       break;
04906    }
04907    case 77: /* M: Mute */ 
04908       user->adminflags |= ADMINFLAG_MUTED;
04909       break;
04910    case 78: /* N: Mute all (non-admin) users */
04911       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_muted_cb, &cnf);
04912       break;               
04913    case 109: /* m: Unmute */ 
04914       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
04915       break;
04916    case 110: /* n: Unmute all users */
04917       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, NULL);
04918       break;
04919    case 107: /* k: Kick user */ 
04920       user->adminflags |= ADMINFLAG_KICKME;
04921       break;
04922    case 118: /* v: Lower all users listen volume */
04923       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_voldown_cb, NULL);
04924       break;
04925    case 86: /* V: Raise all users listen volume */
04926       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_volup_cb, NULL);
04927       break;
04928    case 115: /* s: Lower all users speaking volume */
04929       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_voldown_cb, NULL);
04930       break;
04931    case 83: /* S: Raise all users speaking volume */
04932       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_volup_cb, NULL);
04933       break;
04934    case 82: /* R: Reset all volume levels */
04935       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_reset_vol_cb, NULL);
04936       break;
04937    case 114: /* r: Reset user's volume level */
04938       reset_volumes(user);
04939       break;
04940    case 85: /* U: Raise user's listen volume */
04941       tweak_listen_volume(user, VOL_UP);
04942       break;
04943    case 117: /* u: Lower user's listen volume */
04944       tweak_listen_volume(user, VOL_DOWN);
04945       break;
04946    case 84: /* T: Raise user's talk volume */
04947       tweak_talk_volume(user, VOL_UP);
04948       break;
04949    case 116: /* t: Lower user's talk volume */
04950       tweak_talk_volume(user, VOL_DOWN);
04951       break;
04952    case 'E': /* E: Extend conference */
04953       if (rt_extend_conf(args.confno)) {
04954          res = -1;
04955       }
04956       break;
04957    }
04958 
04959    if (args.user) {
04960       /* decrement reference from find_user */
04961       ao2_ref(user, -1);
04962    }
04963 usernotfound:
04964    AST_LIST_UNLOCK(&confs);
04965 
04966    dispose_conf(cnf);
04967    pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", res == -2 ? "NOTFOUND" : res ? "FAILED" : "OK");
04968 
04969    return 0;
04970 }
04971 
04972 /*! \brief The MeetMeChannelAdmin application 
04973    MeetMeChannelAdmin(channel, command) */
04974 static int channel_admin_exec(struct ast_channel *chan, const char *data) {
04975    char *params;
04976    struct ast_conference *conf = NULL;
04977    struct ast_conf_user *user = NULL;
04978    AST_DECLARE_APP_ARGS(args,
04979       AST_APP_ARG(channel);
04980       AST_APP_ARG(command);
04981    );
04982 
04983    if (ast_strlen_zero(data)) {
04984       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
04985       return -1;
04986    }
04987    
04988    params = ast_strdupa(data);
04989    AST_STANDARD_APP_ARGS(args, params);
04990 
04991    if (!args.channel) {
04992       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
04993       return -1;
04994    }
04995 
04996    if (!args.command) {
04997       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
04998       return -1;
04999    }
05000 
05001    AST_LIST_LOCK(&confs);
05002    AST_LIST_TRAVERSE(&confs, conf, list) {
05003       if ((user = ao2_callback(conf->usercontainer, 0, user_chan_cb, args.channel))) {
05004          break;
05005       }
05006    }
05007    
05008    if (!user) {
05009       ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
05010       AST_LIST_UNLOCK(&confs);
05011       return 0;
05012    }
05013    
05014    /* perform the specified action */
05015    switch (*args.command) {
05016       case 77: /* M: Mute */ 
05017          user->adminflags |= ADMINFLAG_MUTED;
05018          break;
05019       case 109: /* m: Unmute */ 
05020          user->adminflags &= ~ADMINFLAG_MUTED;
05021          break;
05022       case 107: /* k: Kick user */ 
05023          user->adminflags |= ADMINFLAG_KICKME;
05024          break;
05025       default: /* unknown command */
05026          ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
05027          break;
05028    }
05029    ao2_ref(user, -1);
05030    AST_LIST_UNLOCK(&confs);
05031    
05032    return 0;
05033 }
05034 
05035 static int meetmemute(struct mansession *s, const struct message *m, int mute)
05036 {
05037    struct ast_conference *conf;
05038    struct ast_conf_user *user;
05039    const char *confid = astman_get_header(m, "Meetme");
05040    char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
05041    int userno;
05042 
05043    if (ast_strlen_zero(confid)) {
05044       astman_send_error(s, m, "Meetme conference not specified");
05045       return 0;
05046    }
05047 
05048    if (ast_strlen_zero(userid)) {
05049       astman_send_error(s, m, "Meetme user number not specified");
05050       return 0;
05051    }
05052 
05053    userno = strtoul(userid, &userid, 10);
05054 
05055    if (*userid) {
05056       astman_send_error(s, m, "Invalid user number");
05057       return 0;
05058    }
05059 
05060    /* Look in the conference list */
05061    AST_LIST_LOCK(&confs);
05062    AST_LIST_TRAVERSE(&confs, conf, list) {
05063       if (!strcmp(confid, conf->confno))
05064          break;
05065    }
05066 
05067    if (!conf) {
05068       AST_LIST_UNLOCK(&confs);
05069       astman_send_error(s, m, "Meetme conference does not exist");
05070       return 0;
05071    }
05072 
05073    user = ao2_find(conf->usercontainer, &userno, 0);
05074 
05075    if (!user) {
05076       AST_LIST_UNLOCK(&confs);
05077       astman_send_error(s, m, "User number not found");
05078       return 0;
05079    }
05080 
05081    if (mute)
05082       user->adminflags |= ADMINFLAG_MUTED;   /* request user muting */
05083    else
05084       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST); /* request user unmuting */
05085 
05086    AST_LIST_UNLOCK(&confs);
05087 
05088    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);
05089 
05090    ao2_ref(user, -1);
05091    astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
05092    return 0;
05093 }
05094 
05095 static int action_meetmemute(struct mansession *s, const struct message *m)
05096 {
05097    return meetmemute(s, m, 1);
05098 }
05099 
05100 static int action_meetmeunmute(struct mansession *s, const struct message *m)
05101 {
05102    return meetmemute(s, m, 0);
05103 }
05104 
05105 static int action_meetmelist(struct mansession *s, const struct message *m)
05106 {
05107    const char *actionid = astman_get_header(m, "ActionID");
05108    const char *conference = astman_get_header(m, "Conference");
05109    char idText[80] = "";
05110    struct ast_conference *cnf;
05111    struct ast_conf_user *user;
05112    struct ao2_iterator user_iter;
05113    int total = 0;
05114 
05115    if (!ast_strlen_zero(actionid))
05116       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
05117 
05118    if (AST_LIST_EMPTY(&confs)) {
05119       astman_send_error(s, m, "No active conferences.");
05120       return 0;
05121    }
05122 
05123    astman_send_listack(s, m, "Meetme user list will follow", "start");
05124 
05125    /* Find the right conference */
05126    AST_LIST_LOCK(&confs);
05127    AST_LIST_TRAVERSE(&confs, cnf, list) {
05128       /* If we ask for one particular, and this isn't it, skip it */
05129       if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
05130          continue;
05131 
05132       /* Show all the users */
05133       user_iter = ao2_iterator_init(cnf->usercontainer, 0);
05134       while ((user = ao2_iterator_next(&user_iter))) {
05135          total++;
05136          astman_append(s,
05137             "Event: MeetmeList\r\n"
05138             "%s"
05139             "Conference: %s\r\n"
05140             "UserNumber: %d\r\n"
05141             "CallerIDNum: %s\r\n"
05142             "CallerIDName: %s\r\n"
05143             "ConnectedLineNum: %s\r\n"
05144             "ConnectedLineName: %s\r\n"
05145             "Channel: %s\r\n"
05146             "Admin: %s\r\n"
05147             "Role: %s\r\n"
05148             "MarkedUser: %s\r\n"
05149             "Muted: %s\r\n"
05150             "Talking: %s\r\n"
05151             "\r\n",
05152             idText,
05153             cnf->confno,
05154             user->user_no,
05155             S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
05156             S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<no name>"),
05157             S_COR(user->chan->connected.id.number.valid, user->chan->connected.id.number.str, "<unknown>"),
05158             S_COR(user->chan->connected.id.name.valid, user->chan->connected.id.name.str, "<no name>"),
05159             user->chan->name,
05160             ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "Yes" : "No",
05161             ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "Listen only" : ast_test_flag64(&user->userflags, CONFFLAG_TALKER) ? "Talk only" : "Talk and listen",
05162             ast_test_flag64(&user->userflags, CONFFLAG_MARKEDUSER) ? "Yes" : "No",
05163             user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
05164             user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
05165          ao2_ref(user, -1);
05166       }
05167       ao2_iterator_destroy(&user_iter);
05168    }
05169    AST_LIST_UNLOCK(&confs);
05170    /* Send final confirmation */
05171    astman_append(s,
05172    "Event: MeetmeListComplete\r\n"
05173    "EventList: Complete\r\n"
05174    "ListItems: %d\r\n"
05175    "%s"
05176    "\r\n", total, idText);
05177    return 0;
05178 }
05179 
05180 /*! \internal
05181  * \brief creates directory structure and assigns absolute path from relative paths for filenames
05182  *
05183  * \param filename contains the absolute or relative path to the desired file
05184  * \param buffer stores completed filename, absolutely must be a buffer of PATH_MAX length
05185  */
05186 static void filename_parse(char *filename, char *buffer)
05187 {
05188    char *slash;
05189    if (ast_strlen_zero(filename)) {
05190       ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
05191    } else if (filename[0] != '/') {
05192       snprintf(buffer, PATH_MAX, "%s/meetme/%s", ast_config_AST_SPOOL_DIR, filename);
05193    } else {
05194       ast_copy_string(buffer, filename, PATH_MAX);
05195    }
05196 
05197    slash = buffer;
05198    if ((slash = strrchr(slash, '/'))) {
05199       *slash = '\0';
05200       ast_mkdir(buffer, 0777);
05201       *slash = '/';
05202    }
05203 }
05204 
05205 static void *recordthread(void *args)
05206 {
05207    struct ast_conference *cnf = args;
05208    struct ast_frame *f = NULL;
05209    int flags;
05210    struct ast_filestream *s = NULL;
05211    int res = 0;
05212    int x;
05213    const char *oldrecordingfilename = NULL;
05214    char filename_buffer[PATH_MAX];
05215 
05216    if (!cnf || !cnf->lchan) {
05217       pthread_exit(0);
05218    }
05219 
05220    filename_buffer[0] = '\0';
05221    filename_parse(cnf->recordingfilename, filename_buffer);
05222 
05223    ast_stopstream(cnf->lchan);
05224    flags = O_CREAT | O_TRUNC | O_WRONLY;
05225 
05226 
05227    cnf->recording = MEETME_RECORD_ACTIVE;
05228    while (ast_waitfor(cnf->lchan, -1) > -1) {
05229       if (cnf->recording == MEETME_RECORD_TERMINATE) {
05230          AST_LIST_LOCK(&confs);
05231          AST_LIST_UNLOCK(&confs);
05232          break;
05233       }
05234       if (!s && !(ast_strlen_zero(filename_buffer)) && (filename_buffer != oldrecordingfilename)) {
05235          s = ast_writefile(filename_buffer, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
05236          oldrecordingfilename = filename_buffer;
05237       }
05238       
05239       f = ast_read(cnf->lchan);
05240       if (!f) {
05241          res = -1;
05242          break;
05243       }
05244       if (f->frametype == AST_FRAME_VOICE) {
05245          ast_mutex_lock(&cnf->listenlock);
05246          for (x = 0; x < AST_FRAME_BITS; x++) {
05247             /* Free any translations that have occured */
05248             if (cnf->transframe[x]) {
05249                ast_frfree(cnf->transframe[x]);
05250                cnf->transframe[x] = NULL;
05251             }
05252          }
05253          if (cnf->origframe)
05254             ast_frfree(cnf->origframe);
05255          cnf->origframe = ast_frdup(f);
05256          ast_mutex_unlock(&cnf->listenlock);
05257          if (s)
05258             res = ast_writestream(s, f);
05259          if (res) {
05260             ast_frfree(f);
05261             break;
05262          }
05263       }
05264       ast_frfree(f);
05265    }
05266    cnf->recording = MEETME_RECORD_OFF;
05267    if (s)
05268       ast_closestream(s);
05269    
05270    pthread_exit(0);
05271 }
05272 
05273 /*! \brief Callback for devicestate providers */
05274 static enum ast_device_state meetmestate(const char *data)
05275 {
05276    struct ast_conference *conf;
05277 
05278    /* Find conference */
05279    AST_LIST_LOCK(&confs);
05280    AST_LIST_TRAVERSE(&confs, conf, list) {
05281       if (!strcmp(data, conf->confno))
05282          break;
05283    }
05284    AST_LIST_UNLOCK(&confs);
05285    if (!conf)
05286       return AST_DEVICE_INVALID;
05287 
05288 
05289    /* SKREP to fill */
05290    if (!conf->users)
05291       return AST_DEVICE_NOT_INUSE;
05292 
05293    return AST_DEVICE_INUSE;
05294 }
05295 
05296 static void load_config_meetme(void)
05297 {
05298    struct ast_config *cfg;
05299    struct ast_flags config_flags = { 0 };
05300    const char *val;
05301 
05302    if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags))) {
05303       return;
05304    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
05305       ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format.  Aborting.\n");
05306       return;
05307    }
05308 
05309    audio_buffers = DEFAULT_AUDIO_BUFFERS;
05310 
05311    /*  Scheduling support is off by default */
05312    rt_schedule = 0;
05313    fuzzystart = 0;
05314    earlyalert = 0;
05315    endalert = 0;
05316    extendby = 0;
05317 
05318    /*  Logging of participants defaults to ON for compatibility reasons */
05319    rt_log_members = 1;  
05320 
05321    if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
05322       if ((sscanf(val, "%30d", &audio_buffers) != 1)) {
05323          ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
05324          audio_buffers = DEFAULT_AUDIO_BUFFERS;
05325       } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
05326          ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
05327             DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
05328          audio_buffers = DEFAULT_AUDIO_BUFFERS;
05329       }
05330       if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
05331          ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
05332    }
05333 
05334    if ((val = ast_variable_retrieve(cfg, "general", "schedule")))
05335       rt_schedule = ast_true(val);
05336    if ((val = ast_variable_retrieve(cfg, "general", "logmembercount")))
05337       rt_log_members = ast_true(val);
05338    if ((val = ast_variable_retrieve(cfg, "general", "fuzzystart"))) {
05339       if ((sscanf(val, "%30d", &fuzzystart) != 1)) {
05340          ast_log(LOG_WARNING, "fuzzystart must be a number, not '%s'\n", val);
05341          fuzzystart = 0;
05342       } 
05343    }
05344    if ((val = ast_variable_retrieve(cfg, "general", "earlyalert"))) {
05345       if ((sscanf(val, "%30d", &earlyalert) != 1)) {
05346          ast_log(LOG_WARNING, "earlyalert must be a number, not '%s'\n", val);
05347          earlyalert = 0;
05348       } 
05349    }
05350    if ((val = ast_variable_retrieve(cfg, "general", "endalert"))) {
05351       if ((sscanf(val, "%30d", &endalert) != 1)) {
05352          ast_log(LOG_WARNING, "endalert must be a number, not '%s'\n", val);
05353          endalert = 0;
05354       } 
05355    }
05356    if ((val = ast_variable_retrieve(cfg, "general", "extendby"))) {
05357       if ((sscanf(val, "%30d", &extendby) != 1)) {
05358          ast_log(LOG_WARNING, "extendby must be a number, not '%s'\n", val);
05359          extendby = 0;
05360       } 
05361    }
05362 
05363    ast_config_destroy(cfg);
05364 }
05365 
05366 /*!
05367  * \private
05368  * \brief helper for RAII_VAR
05369  */
05370 static void unref_obj(void *obj)
05371 {
05372    if (obj) {
05373       ao2_ref(obj, -1);
05374    }
05375 }
05376 
05377 /*!
05378  * \internal
05379  * \brief Find an SLA trunk by name
05380  */
05381 static struct sla_trunk *sla_find_trunk(const char *name)
05382 {
05383    struct sla_trunk tmp_trunk = {
05384       .name = name,
05385    };
05386 
05387    return ao2_find(sla_trunks, &tmp_trunk, OBJ_POINTER);
05388 }
05389 
05390 /*!
05391  * \internal
05392  * \brief Find an SLA station by name
05393  */
05394 static struct sla_station *sla_find_station(const char *name)
05395 {
05396    struct sla_station tmp_station = {
05397       .name = name,
05398    };
05399 
05400    return ao2_find(sla_stations, &tmp_station, OBJ_POINTER);
05401 }
05402 
05403 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
05404    const struct sla_station *station)
05405 {
05406    struct sla_station_ref *station_ref;
05407    struct sla_trunk_ref *trunk_ref;
05408 
05409    /* For each station that has this call on hold, check for private hold. */
05410    AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
05411       AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
05412          if (trunk_ref->trunk != trunk || station_ref->station == station)
05413             continue;
05414          if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
05415             station_ref->station->hold_access == SLA_HOLD_PRIVATE)
05416             return 1;
05417          return 0;
05418       }
05419    }
05420 
05421    return 0;
05422 }
05423 
05424 /*!
05425  * \brief Find a trunk reference on a station by name
05426  * \param station the station
05427  * \param name the trunk's name
05428  * \pre sla_station is locked
05429  * \return a pointer to the station's trunk reference.  If the trunk
05430  *         is not found, it is not idle and barge is disabled, or if
05431  *         it is on hold and private hold is set, then NULL will be returned.
05432  */
05433 static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
05434    const char *name)
05435 {
05436    struct sla_trunk_ref *trunk_ref = NULL;
05437 
05438    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05439       if (strcasecmp(trunk_ref->trunk->name, name))
05440          continue;
05441 
05442       if ( (trunk_ref->trunk->barge_disabled 
05443          && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
05444          (trunk_ref->trunk->hold_stations 
05445          && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
05446          && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
05447          sla_check_station_hold_access(trunk_ref->trunk, station) ) 
05448       {
05449          trunk_ref = NULL;
05450       }
05451 
05452       break;
05453    }
05454 
05455    if (trunk_ref) {
05456       ao2_ref(trunk_ref, 1);
05457    }
05458 
05459    return trunk_ref;
05460 }
05461 
05462 static void sla_station_ref_destructor(void *obj)
05463 {
05464    struct sla_station_ref *station_ref = obj;
05465 
05466    if (station_ref->station) {
05467       ao2_ref(station_ref->station, -1);
05468       station_ref->station = NULL;
05469    }
05470 }
05471 
05472 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
05473 {
05474    struct sla_station_ref *station_ref;
05475 
05476    if (!(station_ref = ao2_alloc(sizeof(*station_ref), sla_station_ref_destructor))) {
05477       return NULL;
05478    }
05479 
05480    ao2_ref(station, 1);
05481    station_ref->station = station;
05482 
05483    return station_ref;
05484 }
05485 
05486 static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
05487 {
05488    struct sla_ringing_station *ringing_station;
05489 
05490    if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
05491       return NULL;
05492 
05493    ao2_ref(station, 1);
05494    ringing_station->station = station;
05495    ringing_station->ring_begin = ast_tvnow();
05496 
05497    return ringing_station;
05498 }
05499 
05500 static void sla_ringing_station_destroy(struct sla_ringing_station *ringing_station)
05501 {
05502    if (ringing_station->station) {
05503       ao2_ref(ringing_station->station, -1);
05504       ringing_station->station = NULL;
05505    }
05506 
05507    ast_free(ringing_station);
05508 }
05509 
05510 static struct sla_failed_station *sla_create_failed_station(struct sla_station *station)
05511 {
05512    struct sla_failed_station *failed_station;
05513 
05514    if (!(failed_station = ast_calloc(1, sizeof(*failed_station)))) {
05515       return NULL;
05516    }
05517 
05518    ao2_ref(station, 1);
05519    failed_station->station = station;
05520    failed_station->last_try = ast_tvnow();
05521 
05522    return failed_station;
05523 }
05524 
05525 static void sla_failed_station_destroy(struct sla_failed_station *failed_station)
05526 {
05527    if (failed_station->station) {
05528       ao2_ref(failed_station->station, -1);
05529       failed_station->station = NULL;
05530    }
05531 
05532    ast_free(failed_station);
05533 }
05534 
05535 static enum ast_device_state sla_state_to_devstate(enum sla_trunk_state state)
05536 {
05537    switch (state) {
05538    case SLA_TRUNK_STATE_IDLE:
05539       return AST_DEVICE_NOT_INUSE;
05540    case SLA_TRUNK_STATE_RINGING:
05541       return AST_DEVICE_RINGING;
05542    case SLA_TRUNK_STATE_UP:
05543       return AST_DEVICE_INUSE;
05544    case SLA_TRUNK_STATE_ONHOLD:
05545    case SLA_TRUNK_STATE_ONHOLD_BYME:
05546       return AST_DEVICE_ONHOLD;
05547    }
05548 
05549    return AST_DEVICE_UNKNOWN;
05550 }
05551 
05552 static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state, 
05553    enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
05554 {
05555    struct sla_station *station;
05556    struct sla_trunk_ref *trunk_ref;
05557    struct ao2_iterator i;
05558 
05559    i = ao2_iterator_init(sla_stations, 0);
05560    while ((station = ao2_iterator_next(&i))) {
05561       ao2_lock(station);
05562       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05563          if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
05564                || trunk_ref == exclude) {
05565             continue;
05566          }
05567          trunk_ref->state = state;
05568          ast_devstate_changed(sla_state_to_devstate(state), AST_DEVSTATE_CACHABLE,
05569                     "SLA:%s_%s", station->name, trunk->name);
05570          break;
05571       }
05572       ao2_unlock(station);
05573       ao2_ref(station, -1);
05574    }
05575    ao2_iterator_destroy(&i);
05576 }
05577 
05578 struct run_station_args {
05579    struct sla_station *station;
05580    struct sla_trunk_ref *trunk_ref;
05581    ast_mutex_t *cond_lock;
05582    ast_cond_t *cond;
05583 };
05584 
05585 static void answer_trunk_chan(struct ast_channel *chan)
05586 {
05587    ast_answer(chan);
05588    ast_indicate(chan, -1);
05589 }
05590 
05591 static void *run_station(void *data)
05592 {
05593    RAII_VAR(struct sla_station *, station, NULL, unref_obj);
05594    RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, unref_obj);
05595    struct ast_str *conf_name = ast_str_create(16);
05596    struct ast_flags64 conf_flags = { 0 };
05597    struct ast_conference *conf;
05598 
05599    {
05600       struct run_station_args *args = data;
05601       station = args->station;
05602       trunk_ref = args->trunk_ref;
05603       ast_mutex_lock(args->cond_lock);
05604       ast_cond_signal(args->cond);
05605       ast_mutex_unlock(args->cond_lock);
05606       /* args is no longer valid here. */
05607    }
05608 
05609    ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
05610    ast_str_set(&conf_name, 0, "SLA_%s", trunk_ref->trunk->name);
05611    ast_set_flag64(&conf_flags, 
05612       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
05613    answer_trunk_chan(trunk_ref->chan);
05614    conf = build_conf(ast_str_buffer(conf_name), "", "", 0, 0, 1, trunk_ref->chan, NULL);
05615    if (conf) {
05616       conf_run(trunk_ref->chan, conf, &conf_flags, NULL);
05617       dispose_conf(conf);
05618       conf = NULL;
05619    }
05620    trunk_ref->chan = NULL;
05621    if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
05622       trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
05623       ast_str_append(&conf_name, 0, ",K");
05624       admin_exec(NULL, ast_str_buffer(conf_name));
05625       trunk_ref->trunk->hold_stations = 0;
05626       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05627    }
05628 
05629    ast_dial_join(station->dial);
05630    ast_dial_destroy(station->dial);
05631    station->dial = NULL;
05632    ast_free(conf_name);
05633 
05634    return NULL;
05635 }
05636 
05637 static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk);
05638 
05639 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
05640 {
05641    char buf[80];
05642    struct sla_station_ref *station_ref;
05643 
05644    snprintf(buf, sizeof(buf), "SLA_%s,K", ringing_trunk->trunk->name);
05645    admin_exec(NULL, buf);
05646    sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05647 
05648    while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry))) {
05649       ao2_ref(station_ref, -1);
05650    }
05651 
05652    sla_ringing_trunk_destroy(ringing_trunk);
05653 }
05654 
05655 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
05656    enum sla_station_hangup hangup)
05657 {
05658    struct sla_ringing_trunk *ringing_trunk;
05659    struct sla_trunk_ref *trunk_ref;
05660    struct sla_station_ref *station_ref;
05661 
05662    ast_dial_join(ringing_station->station->dial);
05663    ast_dial_destroy(ringing_station->station->dial);
05664    ringing_station->station->dial = NULL;
05665 
05666    if (hangup == SLA_STATION_HANGUP_NORMAL)
05667       goto done;
05668 
05669    /* If the station is being hung up because of a timeout, then add it to the
05670     * list of timed out stations on each of the ringing trunks.  This is so
05671     * that when doing further processing to figure out which stations should be
05672     * ringing, which trunk to answer, determining timeouts, etc., we know which
05673     * ringing trunks we should ignore. */
05674    AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05675       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
05676          if (ringing_trunk->trunk == trunk_ref->trunk)
05677             break;
05678       }
05679       if (!trunk_ref)
05680          continue;
05681       if (!(station_ref = sla_create_station_ref(ringing_station->station)))
05682          continue;
05683       AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
05684    }
05685 
05686 done:
05687    sla_ringing_station_destroy(ringing_station);
05688 }
05689 
05690 static void sla_dial_state_callback(struct ast_dial *dial)
05691 {
05692    sla_queue_event(SLA_EVENT_DIAL_STATE);
05693 }
05694 
05695 /*! \brief Check to see if dialing this station already timed out for this ringing trunk
05696  * \note Assumes sla.lock is locked
05697  */
05698 static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
05699    const struct sla_station *station)
05700 {
05701    struct sla_station_ref *timed_out_station;
05702 
05703    AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
05704       if (station == timed_out_station->station)
05705          return 1;
05706    }
05707 
05708    return 0;
05709 }
05710 
05711 /*! \brief Choose the highest priority ringing trunk for a station
05712  * \param station the station
05713  * \param rm remove the ringing trunk once selected
05714  * \param trunk_ref a place to store the pointer to this stations reference to
05715  *        the selected trunk
05716  * \return a pointer to the selected ringing trunk, or NULL if none found
05717  * \note Assumes that sla.lock is locked
05718  */
05719 static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station, 
05720    struct sla_trunk_ref **trunk_ref, int rm)
05721 {
05722    struct sla_trunk_ref *s_trunk_ref;
05723    struct sla_ringing_trunk *ringing_trunk = NULL;
05724 
05725    AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
05726       AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
05727          /* Make sure this is the trunk we're looking for */
05728          if (s_trunk_ref->trunk != ringing_trunk->trunk)
05729             continue;
05730 
05731          /* This trunk on the station is ringing.  But, make sure this station
05732           * didn't already time out while this trunk was ringing. */
05733          if (sla_check_timed_out_station(ringing_trunk, station))
05734             continue;
05735 
05736          if (rm)
05737             AST_LIST_REMOVE_CURRENT(entry);
05738 
05739          if (trunk_ref) {
05740             ao2_ref(s_trunk_ref, 1);
05741             *trunk_ref = s_trunk_ref;
05742          }
05743 
05744          break;
05745       }
05746       AST_LIST_TRAVERSE_SAFE_END;
05747    
05748       if (ringing_trunk)
05749          break;
05750    }
05751 
05752    return ringing_trunk;
05753 }
05754 
05755 static void sla_handle_dial_state_event(void)
05756 {
05757    struct sla_ringing_station *ringing_station;
05758 
05759    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
05760       RAII_VAR(struct sla_trunk_ref *, s_trunk_ref, NULL, unref_obj);
05761       struct sla_ringing_trunk *ringing_trunk = NULL;
05762       struct run_station_args args;
05763       enum ast_dial_result dial_res;
05764       pthread_t dont_care;
05765       ast_mutex_t cond_lock;
05766       ast_cond_t cond;
05767 
05768       switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
05769       case AST_DIAL_RESULT_HANGUP:
05770       case AST_DIAL_RESULT_INVALID:
05771       case AST_DIAL_RESULT_FAILED:
05772       case AST_DIAL_RESULT_TIMEOUT:
05773       case AST_DIAL_RESULT_UNANSWERED:
05774          AST_LIST_REMOVE_CURRENT(entry);
05775          sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
05776          break;
05777       case AST_DIAL_RESULT_ANSWERED:
05778          AST_LIST_REMOVE_CURRENT(entry);
05779          /* Find the appropriate trunk to answer. */
05780          ast_mutex_lock(&sla.lock);
05781          ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
05782          ast_mutex_unlock(&sla.lock);
05783          if (!ringing_trunk) {
05784             /* This case happens in a bit of a race condition.  If two stations answer
05785              * the outbound call at the same time, the first one will get connected to
05786              * the trunk.  When the second one gets here, it will not see any trunks
05787              * ringing so we have no idea what to conect it to.  So, we just hang up
05788              * on it. */
05789             ast_debug(1, "Found no ringing trunk for station '%s' to answer!\n", ringing_station->station->name);
05790             ast_dial_join(ringing_station->station->dial);
05791             ast_dial_destroy(ringing_station->station->dial);
05792             ringing_station->station->dial = NULL;
05793             sla_ringing_station_destroy(ringing_station);
05794             break;
05795          }
05796          /* Track the channel that answered this trunk */
05797          s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
05798          /* Actually answer the trunk */
05799          answer_trunk_chan(ringing_trunk->trunk->chan);
05800          sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
05801          /* Now, start a thread that will connect this station to the trunk.  The rest of
05802           * the code here sets up the thread and ensures that it is able to save the arguments
05803           * before they are no longer valid since they are allocated on the stack. */
05804          ao2_ref(s_trunk_ref, 1);
05805          args.trunk_ref = s_trunk_ref;
05806          ao2_ref(ringing_station->station, 1);
05807          args.station = ringing_station->station;
05808          args.cond = &cond;
05809          args.cond_lock = &cond_lock;
05810          sla_ringing_trunk_destroy(ringing_trunk);
05811          sla_ringing_station_destroy(ringing_station);
05812          ast_mutex_init(&cond_lock);
05813          ast_cond_init(&cond, NULL);
05814          ast_mutex_lock(&cond_lock);
05815          ast_pthread_create_detached_background(&dont_care, NULL, run_station, &args);
05816          ast_cond_wait(&cond, &cond_lock);
05817          ast_mutex_unlock(&cond_lock);
05818          ast_mutex_destroy(&cond_lock);
05819          ast_cond_destroy(&cond);
05820          break;
05821       case AST_DIAL_RESULT_TRYING:
05822       case AST_DIAL_RESULT_RINGING:
05823       case AST_DIAL_RESULT_PROGRESS:
05824       case AST_DIAL_RESULT_PROCEEDING:
05825          break;
05826       }
05827       if (dial_res == AST_DIAL_RESULT_ANSWERED) {
05828          /* Queue up reprocessing ringing trunks, and then ringing stations again */
05829          sla_queue_event(SLA_EVENT_RINGING_TRUNK);
05830          sla_queue_event(SLA_EVENT_DIAL_STATE);
05831          break;
05832       }
05833    }
05834    AST_LIST_TRAVERSE_SAFE_END;
05835 }
05836 
05837 /*! \brief Check to see if this station is already ringing 
05838  * \note Assumes sla.lock is locked 
05839  */
05840 static int sla_check_ringing_station(const struct sla_station *station)
05841 {
05842    struct sla_ringing_station *ringing_station;
05843 
05844    AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
05845       if (station == ringing_station->station)
05846          return 1;
05847    }
05848 
05849    return 0;
05850 }
05851 
05852 /*! \brief Check to see if this station has failed to be dialed in the past minute
05853  * \note assumes sla.lock is locked
05854  */
05855 static int sla_check_failed_station(const struct sla_station *station)
05856 {
05857    struct sla_failed_station *failed_station;
05858    int res = 0;
05859 
05860    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
05861       if (station != failed_station->station)
05862          continue;
05863       if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
05864          AST_LIST_REMOVE_CURRENT(entry);
05865          sla_failed_station_destroy(failed_station);
05866          break;
05867       }
05868       res = 1;
05869    }
05870    AST_LIST_TRAVERSE_SAFE_END
05871 
05872    return res;
05873 }
05874 
05875 /*! \brief Ring a station
05876  * \note Assumes sla.lock is locked
05877  */
05878 static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
05879 {
05880    char *tech, *tech_data;
05881    struct ast_dial *dial;
05882    struct sla_ringing_station *ringing_station;
05883    enum ast_dial_result res;
05884    int caller_is_saved;
05885    struct ast_party_caller caller;
05886 
05887    if (!(dial = ast_dial_create()))
05888       return -1;
05889 
05890    ast_dial_set_state_callback(dial, sla_dial_state_callback);
05891    tech_data = ast_strdupa(station->device);
05892    tech = strsep(&tech_data, "/");
05893 
05894    if (ast_dial_append(dial, tech, tech_data) == -1) {
05895       ast_dial_destroy(dial);
05896       return -1;
05897    }
05898 
05899    /* Do we need to save off the caller ID data? */
05900    caller_is_saved = 0;
05901    if (!sla.attempt_callerid) {
05902       caller_is_saved = 1;
05903       caller = ringing_trunk->trunk->chan->caller;
05904       ast_party_caller_init(&ringing_trunk->trunk->chan->caller);
05905    }
05906 
05907    res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
05908    
05909    /* Restore saved caller ID */
05910    if (caller_is_saved) {
05911       ast_party_caller_free(&ringing_trunk->trunk->chan->caller);
05912       ringing_trunk->trunk->chan->caller = caller;
05913    }
05914    
05915    if (res != AST_DIAL_RESULT_TRYING) {
05916       struct sla_failed_station *failed_station;
05917       ast_dial_destroy(dial);
05918       if ((failed_station = sla_create_failed_station(station))) {
05919          AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
05920       }
05921       return -1;
05922    }
05923    if (!(ringing_station = sla_create_ringing_station(station))) {
05924       ast_dial_join(dial);
05925       ast_dial_destroy(dial);
05926       return -1;
05927    }
05928 
05929    station->dial = dial;
05930 
05931    AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
05932 
05933    return 0;
05934 }
05935 
05936 /*! \brief Check to see if a station is in use
05937  */
05938 static int sla_check_inuse_station(const struct sla_station *station)
05939 {
05940    struct sla_trunk_ref *trunk_ref;
05941 
05942    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05943       if (trunk_ref->chan)
05944          return 1;
05945    }
05946 
05947    return 0;
05948 }
05949 
05950 static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
05951    const struct sla_trunk *trunk)
05952 {
05953    struct sla_trunk_ref *trunk_ref = NULL;
05954 
05955    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05956       if (trunk_ref->trunk == trunk)
05957          break;
05958    }
05959 
05960    ao2_ref(trunk_ref, 1);
05961 
05962    return trunk_ref;
05963 }
05964 
05965 /*! \brief Calculate the ring delay for a given ringing trunk on a station
05966  * \param station the station
05967  * \param ringing_trunk the trunk.  If NULL, the highest priority ringing trunk will be used
05968  * \return the number of ms left before the delay is complete, or INT_MAX if there is no delay
05969  */
05970 static int sla_check_station_delay(struct sla_station *station, 
05971    struct sla_ringing_trunk *ringing_trunk)
05972 {
05973    RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, unref_obj);
05974    unsigned int delay = UINT_MAX;
05975    int time_left, time_elapsed;
05976 
05977    if (!ringing_trunk)
05978       ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
05979    else
05980       trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
05981 
05982    if (!ringing_trunk || !trunk_ref)
05983       return delay;
05984 
05985    /* If this station has a ring delay specific to the highest priority
05986     * ringing trunk, use that.  Otherwise, use the ring delay specified
05987     * globally for the station. */
05988    delay = trunk_ref->ring_delay;
05989    if (!delay)
05990       delay = station->ring_delay;
05991    if (!delay)
05992       return INT_MAX;
05993 
05994    time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
05995    time_left = (delay * 1000) - time_elapsed;
05996 
05997    return time_left;
05998 }
05999 
06000 /*! \brief Ring stations based on current set of ringing trunks
06001  * \note Assumes that sla.lock is locked
06002  */
06003 static void sla_ring_stations(void)
06004 {
06005    struct sla_station_ref *station_ref;
06006    struct sla_ringing_trunk *ringing_trunk;
06007 
06008    /* Make sure that every station that uses at least one of the ringing
06009     * trunks, is ringing. */
06010    AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
06011       AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
06012          int time_left;
06013 
06014          /* Is this station already ringing? */
06015          if (sla_check_ringing_station(station_ref->station))
06016             continue;
06017 
06018          /* Is this station already in a call? */
06019          if (sla_check_inuse_station(station_ref->station))
06020             continue;
06021 
06022          /* Did we fail to dial this station earlier?  If so, has it been
06023           * a minute since we tried? */
06024          if (sla_check_failed_station(station_ref->station))
06025             continue;
06026 
06027          /* If this station already timed out while this trunk was ringing,
06028           * do not dial it again for this ringing trunk. */
06029          if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
06030             continue;
06031 
06032          /* Check for a ring delay in progress */
06033          time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
06034          if (time_left != INT_MAX && time_left > 0)
06035             continue;
06036 
06037          /* It is time to make this station begin to ring.  Do it! */
06038          sla_ring_station(ringing_trunk, station_ref->station);
06039       }
06040    }
06041    /* Now, all of the stations that should be ringing, are ringing. */
06042 }
06043 
06044 static void sla_hangup_stations(void)
06045 {
06046    struct sla_trunk_ref *trunk_ref;
06047    struct sla_ringing_station *ringing_station;
06048 
06049    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
06050       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
06051          struct sla_ringing_trunk *ringing_trunk;
06052          ast_mutex_lock(&sla.lock);
06053          AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
06054             if (trunk_ref->trunk == ringing_trunk->trunk)
06055                break;
06056          }
06057          ast_mutex_unlock(&sla.lock);
06058          if (ringing_trunk)
06059             break;
06060       }
06061       if (!trunk_ref) {
06062          AST_LIST_REMOVE_CURRENT(entry);
06063          ast_dial_join(ringing_station->station->dial);
06064          ast_dial_destroy(ringing_station->station->dial);
06065          ringing_station->station->dial = NULL;
06066          sla_ringing_station_destroy(ringing_station);
06067       }
06068    }
06069    AST_LIST_TRAVERSE_SAFE_END
06070 }
06071 
06072 static void sla_handle_ringing_trunk_event(void)
06073 {
06074    ast_mutex_lock(&sla.lock);
06075    sla_ring_stations();
06076    ast_mutex_unlock(&sla.lock);
06077 
06078    /* Find stations that shouldn't be ringing anymore. */
06079    sla_hangup_stations();
06080 }
06081 
06082 static void sla_handle_hold_event(struct sla_event *event)
06083 {
06084    ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
06085    event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
06086    ast_devstate_changed(AST_DEVICE_ONHOLD, AST_DEVSTATE_CACHABLE, "SLA:%s_%s",
06087               event->station->name, event->trunk_ref->trunk->name);
06088    sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD, 
06089       INACTIVE_TRUNK_REFS, event->trunk_ref);
06090 
06091    if (event->trunk_ref->trunk->active_stations == 1) {
06092       /* The station putting it on hold is the only one on the call, so start
06093        * Music on hold to the trunk. */
06094       event->trunk_ref->trunk->on_hold = 1;
06095       ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
06096    }
06097 
06098    ast_softhangup(event->trunk_ref->chan, AST_SOFTHANGUP_DEV);
06099    event->trunk_ref->chan = NULL;
06100 }
06101 
06102 /*! \brief Process trunk ring timeouts
06103  * \note Called with sla.lock locked
06104  * \return non-zero if a change to the ringing trunks was made
06105  */
06106 static int sla_calc_trunk_timeouts(unsigned int *timeout)
06107 {
06108    struct sla_ringing_trunk *ringing_trunk;
06109    int res = 0;
06110 
06111    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06112       int time_left, time_elapsed;
06113       if (!ringing_trunk->trunk->ring_timeout)
06114          continue;
06115       time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
06116       time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
06117       if (time_left <= 0) {
06118          pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
06119          AST_LIST_REMOVE_CURRENT(entry);
06120          sla_stop_ringing_trunk(ringing_trunk);
06121          res = 1;
06122          continue;
06123       }
06124       if (time_left < *timeout)
06125          *timeout = time_left;
06126    }
06127    AST_LIST_TRAVERSE_SAFE_END;
06128 
06129    return res;
06130 }
06131 
06132 /*! \brief Process station ring timeouts
06133  * \note Called with sla.lock locked
06134  * \return non-zero if a change to the ringing stations was made
06135  */
06136 static int sla_calc_station_timeouts(unsigned int *timeout)
06137 {
06138    struct sla_ringing_trunk *ringing_trunk;
06139    struct sla_ringing_station *ringing_station;
06140    int res = 0;
06141 
06142    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
06143       unsigned int ring_timeout = 0;
06144       int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
06145       struct sla_trunk_ref *trunk_ref;
06146 
06147       /* If there are any ring timeouts specified for a specific trunk
06148        * on the station, then use the highest per-trunk ring timeout.
06149        * Otherwise, use the ring timeout set for the entire station. */
06150       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
06151          struct sla_station_ref *station_ref;
06152          int trunk_time_elapsed, trunk_time_left;
06153 
06154          AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
06155             if (ringing_trunk->trunk == trunk_ref->trunk)
06156                break;
06157          }
06158          if (!ringing_trunk)
06159             continue;
06160 
06161          /* If there is a trunk that is ringing without a timeout, then the
06162           * only timeout that could matter is a global station ring timeout. */
06163          if (!trunk_ref->ring_timeout)
06164             break;
06165 
06166          /* This trunk on this station is ringing and has a timeout.
06167           * However, make sure this trunk isn't still ringing from a
06168           * previous timeout.  If so, don't consider it. */
06169          AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
06170             if (station_ref->station == ringing_station->station)
06171                break;
06172          }
06173          if (station_ref)
06174             continue;
06175 
06176          trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
06177          trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
06178          if (trunk_time_left > final_trunk_time_left)
06179             final_trunk_time_left = trunk_time_left;
06180       }
06181 
06182       /* No timeout was found for ringing trunks, and no timeout for the entire station */
06183       if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
06184          continue;
06185 
06186       /* Compute how much time is left for a global station timeout */
06187       if (ringing_station->station->ring_timeout) {
06188          ring_timeout = ringing_station->station->ring_timeout;
06189          time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
06190          time_left = (ring_timeout * 1000) - time_elapsed;
06191       }
06192 
06193       /* If the time left based on the per-trunk timeouts is smaller than the
06194        * global station ring timeout, use that. */
06195       if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
06196          time_left = final_trunk_time_left;
06197 
06198       /* If there is no time left, the station needs to stop ringing */
06199       if (time_left <= 0) {
06200          AST_LIST_REMOVE_CURRENT(entry);
06201          sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
06202          res = 1;
06203          continue;
06204       }
06205 
06206       /* There is still some time left for this station to ring, so save that
06207        * timeout if it is the first event scheduled to occur */
06208       if (time_left < *timeout)
06209          *timeout = time_left;
06210    }
06211    AST_LIST_TRAVERSE_SAFE_END;
06212 
06213    return res;
06214 }
06215 
06216 /*! \brief Calculate the ring delay for a station
06217  * \note Assumes sla.lock is locked
06218  */
06219 static int sla_calc_station_delays(unsigned int *timeout)
06220 {
06221    struct sla_station *station;
06222    int res = 0;
06223    struct ao2_iterator i;
06224 
06225    i = ao2_iterator_init(sla_stations, 0);
06226    for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
06227       struct sla_ringing_trunk *ringing_trunk;
06228       int time_left;
06229 
06230       /* Ignore stations already ringing */
06231       if (sla_check_ringing_station(station))
06232          continue;
06233 
06234       /* Ignore stations already on a call */
06235       if (sla_check_inuse_station(station))
06236          continue;
06237 
06238       /* Ignore stations that don't have one of their trunks ringing */
06239       if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
06240          continue;
06241 
06242       if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
06243          continue;
06244 
06245       /* If there is no time left, then the station needs to start ringing.
06246        * Return non-zero so that an event will be queued up an event to 
06247        * make that happen. */
06248       if (time_left <= 0) {
06249          res = 1;
06250          continue;
06251       }
06252 
06253       if (time_left < *timeout)
06254          *timeout = time_left;
06255    }
06256    ao2_iterator_destroy(&i);
06257 
06258    return res;
06259 }
06260 
06261 /*! \brief Calculate the time until the next known event
06262  *  \note Called with sla.lock locked */
06263 static int sla_process_timers(struct timespec *ts)
06264 {
06265    unsigned int timeout = UINT_MAX;
06266    struct timeval wait;
06267    unsigned int change_made = 0;
06268 
06269    /* Check for ring timeouts on ringing trunks */
06270    if (sla_calc_trunk_timeouts(&timeout))
06271       change_made = 1;
06272 
06273    /* Check for ring timeouts on ringing stations */
06274    if (sla_calc_station_timeouts(&timeout))
06275       change_made = 1;
06276 
06277    /* Check for station ring delays */
06278    if (sla_calc_station_delays(&timeout))
06279       change_made = 1;
06280 
06281    /* queue reprocessing of ringing trunks */
06282    if (change_made)
06283       sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
06284 
06285    /* No timeout */
06286    if (timeout == UINT_MAX)
06287       return 0;
06288 
06289    if (ts) {
06290       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
06291       ts->tv_sec = wait.tv_sec;
06292       ts->tv_nsec = wait.tv_usec * 1000;
06293    }
06294 
06295    return 1;
06296 }
06297 
06298 static void sla_event_destroy(struct sla_event *event)
06299 {
06300    if (event->trunk_ref) {
06301       ao2_ref(event->trunk_ref, -1);
06302       event->trunk_ref = NULL;
06303    }
06304 
06305    if (event->station) {
06306       ao2_ref(event->station, -1);
06307       event->station = NULL;
06308    }
06309 
06310    ast_free(event);
06311 }
06312 
06313 static void *sla_thread(void *data)
06314 {
06315    struct sla_failed_station *failed_station;
06316    struct sla_ringing_station *ringing_station;
06317 
06318    ast_mutex_lock(&sla.lock);
06319 
06320    while (!sla.stop) {
06321       struct sla_event *event;
06322       struct timespec ts = { 0, };
06323       unsigned int have_timeout = 0;
06324 
06325       if (AST_LIST_EMPTY(&sla.event_q)) {
06326          if ((have_timeout = sla_process_timers(&ts)))
06327             ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
06328          else
06329             ast_cond_wait(&sla.cond, &sla.lock);
06330          if (sla.stop)
06331             break;
06332       }
06333 
06334       if (have_timeout)
06335          sla_process_timers(NULL);
06336 
06337       while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
06338          ast_mutex_unlock(&sla.lock);
06339          switch (event->type) {
06340          case SLA_EVENT_HOLD:
06341             sla_handle_hold_event(event);
06342             break;
06343          case SLA_EVENT_DIAL_STATE:
06344             sla_handle_dial_state_event();
06345             break;
06346          case SLA_EVENT_RINGING_TRUNK:
06347             sla_handle_ringing_trunk_event();
06348             break;
06349          }
06350          sla_event_destroy(event);
06351          ast_mutex_lock(&sla.lock);
06352       }
06353    }
06354 
06355    ast_mutex_unlock(&sla.lock);
06356 
06357    while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry))) {
06358       sla_ringing_station_destroy(ringing_station);
06359    }
06360 
06361    while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry))) {
06362       sla_failed_station_destroy(failed_station);
06363    }
06364 
06365    return NULL;
06366 }
06367 
06368 struct dial_trunk_args {
06369    struct sla_trunk_ref *trunk_ref;
06370    struct sla_station *station;
06371    ast_mutex_t *cond_lock;
06372    ast_cond_t *cond;
06373 };
06374 
06375 static void *dial_trunk(void *data)
06376 {
06377    struct dial_trunk_args *args = data;
06378    struct ast_dial *dial;
06379    char *tech, *tech_data;
06380    enum ast_dial_result dial_res;
06381    char conf_name[MAX_CONFNUM];
06382    struct ast_conference *conf;
06383    struct ast_flags64 conf_flags = { 0 };
06384    RAII_VAR(struct sla_trunk_ref *, trunk_ref, args->trunk_ref, unref_obj);
06385    RAII_VAR(struct sla_station *, station, args->station, unref_obj);
06386    int caller_is_saved;
06387    struct ast_party_caller caller;
06388    int last_state = 0;
06389    int current_state = 0;
06390 
06391    if (!(dial = ast_dial_create())) {
06392       ast_mutex_lock(args->cond_lock);
06393       ast_cond_signal(args->cond);
06394       ast_mutex_unlock(args->cond_lock);
06395       return NULL;
06396    }
06397 
06398    tech_data = ast_strdupa(trunk_ref->trunk->device);
06399    tech = strsep(&tech_data, "/");
06400    if (ast_dial_append(dial, tech, tech_data) == -1) {
06401       ast_mutex_lock(args->cond_lock);
06402       ast_cond_signal(args->cond);
06403       ast_mutex_unlock(args->cond_lock);
06404       ast_dial_destroy(dial);
06405       return NULL;
06406    }
06407 
06408    /* Do we need to save of the caller ID data? */
06409    caller_is_saved = 0;
06410    if (!sla.attempt_callerid) {
06411       caller_is_saved = 1;
06412       caller = trunk_ref->chan->caller;
06413       ast_party_caller_init(&trunk_ref->chan->caller);
06414    }
06415 
06416    dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
06417 
06418    /* Restore saved caller ID */
06419    if (caller_is_saved) {
06420       ast_party_caller_free(&trunk_ref->chan->caller);
06421       trunk_ref->chan->caller = caller;
06422    }
06423 
06424    if (dial_res != AST_DIAL_RESULT_TRYING) {
06425       ast_mutex_lock(args->cond_lock);
06426       ast_cond_signal(args->cond);
06427       ast_mutex_unlock(args->cond_lock);
06428       ast_dial_destroy(dial);
06429       return NULL;
06430    }
06431 
06432    for (;;) {
06433       unsigned int done = 0;
06434       switch ((dial_res = ast_dial_state(dial))) {
06435       case AST_DIAL_RESULT_ANSWERED:
06436          trunk_ref->trunk->chan = ast_dial_answered(dial);
06437       case AST_DIAL_RESULT_HANGUP:
06438       case AST_DIAL_RESULT_INVALID:
06439       case AST_DIAL_RESULT_FAILED:
06440       case AST_DIAL_RESULT_TIMEOUT:
06441       case AST_DIAL_RESULT_UNANSWERED:
06442          done = 1;
06443          break;
06444       case AST_DIAL_RESULT_TRYING:
06445          current_state = AST_CONTROL_PROGRESS;
06446          break;
06447       case AST_DIAL_RESULT_RINGING:
06448       case AST_DIAL_RESULT_PROGRESS:
06449       case AST_DIAL_RESULT_PROCEEDING:
06450          current_state = AST_CONTROL_RINGING;
06451          break;
06452       }
06453       if (done)
06454          break;
06455 
06456       /* check that SLA station that originated trunk call is still alive */
06457       if (station && ast_device_state(station->device) == AST_DEVICE_NOT_INUSE) {
06458          ast_debug(3, "Originating station device %s no longer active\n", station->device);
06459          trunk_ref->trunk->chan = NULL;
06460          break;
06461       }
06462 
06463       /* If trunk line state changed, send indication back to originating SLA Station channel */
06464       if (current_state != last_state) {
06465          ast_debug(3, "Indicating State Change %d to channel %s\n", current_state, trunk_ref->chan->name);
06466          ast_indicate(trunk_ref->chan, current_state);
06467          last_state = current_state;
06468       }
06469 
06470       /* avoid tight loop... sleep for 1/10th second */
06471       ast_safe_sleep(trunk_ref->chan, 100);
06472    }
06473 
06474    if (!trunk_ref->trunk->chan) {
06475       ast_mutex_lock(args->cond_lock);
06476       ast_cond_signal(args->cond);
06477       ast_mutex_unlock(args->cond_lock);
06478       ast_dial_join(dial);
06479       ast_dial_destroy(dial);
06480       return NULL;
06481    }
06482 
06483    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06484    ast_set_flag64(&conf_flags, 
06485       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | 
06486       CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
06487    conf = build_conf(conf_name, "", "", 1, 1, 1, trunk_ref->trunk->chan, NULL);
06488 
06489    ast_mutex_lock(args->cond_lock);
06490    ast_cond_signal(args->cond);
06491    ast_mutex_unlock(args->cond_lock);
06492 
06493    if (conf) {
06494       conf_run(trunk_ref->trunk->chan, conf, &conf_flags, NULL);
06495       dispose_conf(conf);
06496       conf = NULL;
06497    }
06498 
06499    /* If the trunk is going away, it is definitely now IDLE. */
06500    sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06501 
06502    trunk_ref->trunk->chan = NULL;
06503    trunk_ref->trunk->on_hold = 0;
06504 
06505    ast_dial_join(dial);
06506    ast_dial_destroy(dial);
06507 
06508    return NULL;
06509 }
06510 
06511 /*!
06512  * \brief For a given station, choose the highest priority idle trunk
06513  * \pre sla_station is locked
06514  */
06515 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
06516 {
06517    struct sla_trunk_ref *trunk_ref = NULL;
06518 
06519    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06520       if (trunk_ref->state == SLA_TRUNK_STATE_IDLE) {
06521          ao2_ref(trunk_ref, 1);
06522          break;
06523       }
06524    }
06525 
06526    return trunk_ref;
06527 }
06528 
06529 static int sla_station_exec(struct ast_channel *chan, const char *data)
06530 {
06531    char *station_name, *trunk_name;
06532    RAII_VAR(struct sla_station *, station, NULL, unref_obj);
06533    RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, unref_obj);
06534    char conf_name[MAX_CONFNUM];
06535    struct ast_flags64 conf_flags = { 0 };
06536    struct ast_conference *conf;
06537 
06538    if (ast_strlen_zero(data)) {
06539       ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06540       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06541       return 0;
06542    }
06543 
06544    trunk_name = ast_strdupa(data);
06545    station_name = strsep(&trunk_name, "_");
06546 
06547    if (ast_strlen_zero(station_name)) {
06548       ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06549       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06550       return 0;
06551    }
06552 
06553    station = sla_find_station(station_name);
06554 
06555    if (!station) {
06556       ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
06557       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06558       return 0;
06559    }
06560 
06561    ao2_lock(station);
06562    if (!ast_strlen_zero(trunk_name)) {
06563       trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
06564    } else {
06565       trunk_ref = sla_choose_idle_trunk(station);
06566    }
06567    ao2_unlock(station);
06568 
06569    if (!trunk_ref) {
06570       if (ast_strlen_zero(trunk_name))
06571          ast_log(LOG_NOTICE, "No trunks available for call.\n");
06572       else {
06573          ast_log(LOG_NOTICE, "Can't join existing call on trunk "
06574             "'%s' due to access controls.\n", trunk_name);
06575       }
06576       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06577       return 0;
06578    }
06579 
06580    if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
06581       if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
06582          sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06583       else {
06584          trunk_ref->state = SLA_TRUNK_STATE_UP;
06585          ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE,
06586                     "SLA:%s_%s", station->name, trunk_ref->trunk->name);
06587       }
06588    } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
06589       struct sla_ringing_trunk *ringing_trunk;
06590 
06591       ast_mutex_lock(&sla.lock);
06592       AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06593          if (ringing_trunk->trunk == trunk_ref->trunk) {
06594             AST_LIST_REMOVE_CURRENT(entry);
06595             break;
06596          }
06597       }
06598       AST_LIST_TRAVERSE_SAFE_END
06599       ast_mutex_unlock(&sla.lock);
06600 
06601       if (ringing_trunk) {
06602          answer_trunk_chan(ringing_trunk->trunk->chan);
06603          sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06604 
06605          sla_ringing_trunk_destroy(ringing_trunk);
06606 
06607          /* Queue up reprocessing ringing trunks, and then ringing stations again */
06608          sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06609          sla_queue_event(SLA_EVENT_DIAL_STATE);
06610       }
06611    }
06612 
06613    trunk_ref->chan = chan;
06614 
06615    if (!trunk_ref->trunk->chan) {
06616       ast_mutex_t cond_lock;
06617       ast_cond_t cond;
06618       pthread_t dont_care;
06619       struct dial_trunk_args args = {
06620          .trunk_ref = trunk_ref,
06621          .station = station,
06622          .cond_lock = &cond_lock,
06623          .cond = &cond,
06624       };
06625       ao2_ref(trunk_ref, 1);
06626       ao2_ref(station, 1);
06627       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06628       /* Create a thread to dial the trunk and dump it into the conference.
06629        * However, we want to wait until the trunk has been dialed and the
06630        * conference is created before continuing on here. */
06631       ast_autoservice_start(chan);
06632       ast_mutex_init(&cond_lock);
06633       ast_cond_init(&cond, NULL);
06634       ast_mutex_lock(&cond_lock);
06635       ast_pthread_create_detached_background(&dont_care, NULL, dial_trunk, &args);
06636       ast_cond_wait(&cond, &cond_lock);
06637       ast_mutex_unlock(&cond_lock);
06638       ast_mutex_destroy(&cond_lock);
06639       ast_cond_destroy(&cond);
06640       ast_autoservice_stop(chan);
06641       if (!trunk_ref->trunk->chan) {
06642          ast_debug(1, "Trunk didn't get created. chan: %lx\n", (unsigned long) trunk_ref->trunk->chan);
06643          pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06644          sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06645          trunk_ref->chan = NULL;
06646          return 0;
06647       }
06648    }
06649 
06650    if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
06651       trunk_ref->trunk->on_hold) {
06652       trunk_ref->trunk->on_hold = 0;
06653       ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
06654       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06655    }
06656 
06657    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06658    ast_set_flag64(&conf_flags, 
06659       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
06660    ast_answer(chan);
06661    conf = build_conf(conf_name, "", "", 0, 0, 1, chan, NULL);
06662    if (conf) {
06663       conf_run(chan, conf, &conf_flags, NULL);
06664       dispose_conf(conf);
06665       conf = NULL;
06666    }
06667    trunk_ref->chan = NULL;
06668    if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
06669       trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
06670       strncat(conf_name, ",K", sizeof(conf_name) - strlen(conf_name) - 1);
06671       admin_exec(NULL, conf_name);
06672       trunk_ref->trunk->hold_stations = 0;
06673       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06674    }
06675    
06676    pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
06677 
06678    return 0;
06679 }
06680 
06681 static void sla_trunk_ref_destructor(void *obj)
06682 {
06683    struct sla_trunk_ref *trunk_ref = obj;
06684 
06685    if (trunk_ref->trunk) {
06686       ao2_ref(trunk_ref->trunk, -1);
06687       trunk_ref->trunk = NULL;
06688    }
06689 }
06690 
06691 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
06692 {
06693    struct sla_trunk_ref *trunk_ref;
06694 
06695    if (!(trunk_ref = ao2_alloc(sizeof(*trunk_ref), sla_trunk_ref_destructor))) {
06696       return NULL;
06697    }
06698 
06699    ao2_ref(trunk, 1);
06700    trunk_ref->trunk = trunk;
06701 
06702    return trunk_ref;
06703 }
06704 
06705 static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
06706 {
06707    struct sla_ringing_trunk *ringing_trunk;
06708 
06709    if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk)))) {
06710       return NULL;
06711    }
06712 
06713    ao2_ref(trunk, 1);
06714    ringing_trunk->trunk = trunk;
06715    ringing_trunk->ring_begin = ast_tvnow();
06716 
06717    sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
06718 
06719    ast_mutex_lock(&sla.lock);
06720    AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
06721    ast_mutex_unlock(&sla.lock);
06722 
06723    sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06724 
06725    return ringing_trunk;
06726 }
06727 
06728 static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk)
06729 {
06730    if (ringing_trunk->trunk) {
06731       ao2_ref(ringing_trunk->trunk, -1);
06732       ringing_trunk->trunk = NULL;
06733    }
06734 
06735    ast_free(ringing_trunk);
06736 }
06737 
06738 enum {
06739    SLA_TRUNK_OPT_MOH = (1 << 0),
06740 };
06741 
06742 enum {
06743    SLA_TRUNK_OPT_ARG_MOH_CLASS = 0,
06744    SLA_TRUNK_OPT_ARG_ARRAY_SIZE = 1,
06745 };
06746 
06747 AST_APP_OPTIONS(sla_trunk_opts, BEGIN_OPTIONS
06748    AST_APP_OPTION_ARG('M', SLA_TRUNK_OPT_MOH, SLA_TRUNK_OPT_ARG_MOH_CLASS),
06749 END_OPTIONS );
06750 
06751 static int sla_trunk_exec(struct ast_channel *chan, const char *data)
06752 {
06753    char conf_name[MAX_CONFNUM];
06754    struct ast_conference *conf;
06755    struct ast_flags64 conf_flags = { 0 };
06756    RAII_VAR(struct sla_trunk *, trunk, NULL, unref_obj);
06757    struct sla_ringing_trunk *ringing_trunk;
06758    AST_DECLARE_APP_ARGS(args,
06759       AST_APP_ARG(trunk_name);
06760       AST_APP_ARG(options);
06761    );
06762    char *opts[SLA_TRUNK_OPT_ARG_ARRAY_SIZE] = { NULL, };
06763    struct ast_flags opt_flags = { 0 };
06764    char *parse;
06765 
06766    if (ast_strlen_zero(data)) {
06767       ast_log(LOG_ERROR, "The SLATrunk application requires an argument, the trunk name\n");
06768       return -1;
06769    }
06770 
06771    parse = ast_strdupa(data);
06772    AST_STANDARD_APP_ARGS(args, parse);
06773    if (args.argc == 2) {
06774       if (ast_app_parse_options(sla_trunk_opts, &opt_flags, opts, args.options)) {
06775          ast_log(LOG_ERROR, "Error parsing options for SLATrunk\n");
06776          return -1;
06777       }
06778    }
06779 
06780    trunk = sla_find_trunk(args.trunk_name);
06781 
06782    if (!trunk) {
06783       ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", args.trunk_name);
06784       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06785       return 0;
06786    }
06787 
06788    if (trunk->chan) {
06789       ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
06790          args.trunk_name);
06791       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06792       return 0;
06793    }
06794 
06795    trunk->chan = chan;
06796 
06797    if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
06798       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06799       return 0;
06800    }
06801 
06802    snprintf(conf_name, sizeof(conf_name), "SLA_%s", args.trunk_name);
06803    conf = build_conf(conf_name, "", "", 1, 1, 1, chan, NULL);
06804    if (!conf) {
06805       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06806       return 0;
06807    }
06808    ast_set_flag64(&conf_flags, 
06809       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF | CONFFLAG_NO_AUDIO_UNTIL_UP);
06810 
06811    if (ast_test_flag(&opt_flags, SLA_TRUNK_OPT_MOH)) {
06812       ast_indicate(chan, -1);
06813       ast_set_flag64(&conf_flags, CONFFLAG_MOH);
06814    } else
06815       ast_indicate(chan, AST_CONTROL_RINGING);
06816 
06817    conf_run(chan, conf, &conf_flags, opts);
06818    dispose_conf(conf);
06819    conf = NULL;
06820    trunk->chan = NULL;
06821    trunk->on_hold = 0;
06822 
06823    sla_change_trunk_state(trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06824 
06825    if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
06826       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
06827 
06828    /* Remove the entry from the list of ringing trunks if it is still there. */
06829    ast_mutex_lock(&sla.lock);
06830    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06831       if (ringing_trunk->trunk == trunk) {
06832          AST_LIST_REMOVE_CURRENT(entry);
06833          break;
06834       }
06835    }
06836    AST_LIST_TRAVERSE_SAFE_END;
06837    ast_mutex_unlock(&sla.lock);
06838    if (ringing_trunk) {
06839       sla_ringing_trunk_destroy(ringing_trunk);
06840       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
06841       /* Queue reprocessing of ringing trunks to make stations stop ringing
06842        * that shouldn't be ringing after this trunk stopped. */
06843       sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06844    }
06845 
06846    return 0;
06847 }
06848 
06849 static enum ast_device_state sla_state(const char *data)
06850 {
06851    char *buf, *station_name, *trunk_name;
06852    RAII_VAR(struct sla_station *, station, NULL, unref_obj);
06853    struct sla_trunk_ref *trunk_ref;
06854    enum ast_device_state res = AST_DEVICE_INVALID;
06855 
06856    trunk_name = buf = ast_strdupa(data);
06857    station_name = strsep(&trunk_name, "_");
06858 
06859    station = sla_find_station(station_name);
06860    if (station) {
06861       ao2_lock(station);
06862       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06863          if (!strcasecmp(trunk_name, trunk_ref->trunk->name)) {
06864             res = sla_state_to_devstate(trunk_ref->state);
06865             break;
06866          }
06867       }
06868       ao2_unlock(station);
06869    }
06870 
06871    if (res == AST_DEVICE_INVALID) {
06872       ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
06873          trunk_name, station_name);
06874    }
06875 
06876    return res;
06877 }
06878 
06879 static int sla_trunk_release_refs(void *obj, void *arg, int flags)
06880 {
06881    struct sla_trunk *trunk = obj;
06882    struct sla_station_ref *station_ref;
06883 
06884    while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry))) {
06885       ao2_ref(station_ref, -1);
06886    }
06887 
06888    return 0;
06889 }
06890 
06891 static int sla_station_release_refs(void *obj, void *arg, int flags)
06892 {
06893    struct sla_station *station = obj;
06894    struct sla_trunk_ref *trunk_ref;
06895 
06896    while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry))) {
06897       ao2_ref(trunk_ref, -1);
06898    }
06899 
06900    return 0;
06901 }
06902 
06903 static void sla_station_destructor(void *obj)
06904 {
06905    struct sla_station *station = obj;
06906 
06907    ast_debug(1, "sla_station destructor for '%s'\n", station->name);
06908 
06909    if (!ast_strlen_zero(station->autocontext)) {
06910       struct sla_trunk_ref *trunk_ref;
06911 
06912       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06913          char exten[AST_MAX_EXTENSION];
06914          char hint[AST_MAX_APP];
06915          snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
06916          snprintf(hint, sizeof(hint), "SLA:%s", exten);
06917          ast_context_remove_extension(station->autocontext, exten, 
06918             1, sla_registrar);
06919          ast_context_remove_extension(station->autocontext, hint, 
06920             PRIORITY_HINT, sla_registrar);
06921       }
06922    }
06923 
06924    sla_station_release_refs(station, NULL, 0);
06925 
06926    ast_string_field_free_memory(station);
06927 }
06928 
06929 static int sla_trunk_hash(const void *obj, const int flags)
06930 {
06931    const struct sla_trunk *trunk = obj;
06932 
06933    return ast_str_case_hash(trunk->name);
06934 }
06935 
06936 static int sla_trunk_cmp(void *obj, void *arg, int flags)
06937 {
06938    struct sla_trunk *trunk = obj, *trunk2 = arg;
06939 
06940    return !strcasecmp(trunk->name, trunk2->name) ? CMP_MATCH | CMP_STOP : 0;
06941 }
06942 
06943 static int sla_station_hash(const void *obj, const int flags)
06944 {
06945    const struct sla_station *station = obj;
06946 
06947    return ast_str_case_hash(station->name);
06948 }
06949 
06950 static int sla_station_cmp(void *obj, void *arg, int flags)
06951 {
06952    struct sla_station *station = obj, *station2 = arg;
06953 
06954    return !strcasecmp(station->name, station2->name) ? CMP_MATCH | CMP_STOP : 0;
06955 }
06956 
06957 static void sla_destroy(void)
06958 {
06959    if (sla.thread != AST_PTHREADT_NULL) {
06960       ast_mutex_lock(&sla.lock);
06961       sla.stop = 1;
06962       ast_cond_signal(&sla.cond);
06963       ast_mutex_unlock(&sla.lock);
06964       pthread_join(sla.thread, NULL);
06965    }
06966 
06967    /* Drop any created contexts from the dialplan */
06968    ast_context_destroy(NULL, sla_registrar);
06969 
06970    ast_mutex_destroy(&sla.lock);
06971    ast_cond_destroy(&sla.cond);
06972 
06973    ao2_callback(sla_trunks, 0, sla_trunk_release_refs, NULL);
06974    ao2_callback(sla_stations, 0, sla_station_release_refs, NULL);
06975 
06976    ao2_ref(sla_trunks, -1);
06977    sla_trunks = NULL;
06978 
06979    ao2_ref(sla_stations, -1);
06980    sla_stations = NULL;
06981 }
06982 
06983 static int sla_check_device(const char *device)
06984 {
06985    char *tech, *tech_data;
06986 
06987    tech_data = ast_strdupa(device);
06988    tech = strsep(&tech_data, "/");
06989 
06990    if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
06991       return -1;
06992 
06993    return 0;
06994 }
06995 
06996 static void sla_trunk_destructor(void *obj)
06997 {
06998    struct sla_trunk *trunk = obj;
06999 
07000    ast_debug(1, "sla_trunk destructor for '%s'\n", trunk->name);
07001 
07002    if (!ast_strlen_zero(trunk->autocontext)) {
07003       ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
07004    }
07005 
07006    sla_trunk_release_refs(trunk, NULL, 0);
07007 
07008    ast_string_field_free_memory(trunk);
07009 }
07010 
07011 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
07012 {
07013    RAII_VAR(struct sla_trunk *, trunk, NULL, unref_obj);
07014    struct ast_variable *var;
07015    const char *dev;
07016    int existing_trunk = 0;
07017 
07018    if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
07019       ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
07020       return -1;
07021    }
07022 
07023    if (sla_check_device(dev)) {
07024       ast_log(LOG_ERROR, "SLA Trunk '%s' defined with invalid device '%s'!\n",
07025          cat, dev);
07026       return -1;
07027    }
07028 
07029    if ((trunk = sla_find_trunk(cat))) {
07030       trunk->mark = 0;
07031       existing_trunk = 1;
07032    } else if ((trunk = ao2_alloc(sizeof(*trunk), sla_trunk_destructor))) {
07033       if (ast_string_field_init(trunk, 32)) {
07034          return -1;
07035       }
07036       ast_string_field_set(trunk, name, cat);
07037    } else {
07038       return -1;
07039    }
07040 
07041    ao2_lock(trunk);
07042 
07043    ast_string_field_set(trunk, device, dev);
07044 
07045    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
07046       if (!strcasecmp(var->name, "autocontext"))
07047          ast_string_field_set(trunk, autocontext, var->value);
07048       else if (!strcasecmp(var->name, "ringtimeout")) {
07049          if (sscanf(var->value, "%30u", &trunk->ring_timeout) != 1) {
07050             ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
07051                var->value, trunk->name);
07052             trunk->ring_timeout = 0;
07053          }
07054       } else if (!strcasecmp(var->name, "barge"))
07055          trunk->barge_disabled = ast_false(var->value);
07056       else if (!strcasecmp(var->name, "hold")) {
07057          if (!strcasecmp(var->value, "private"))
07058             trunk->hold_access = SLA_HOLD_PRIVATE;
07059          else if (!strcasecmp(var->value, "open"))
07060             trunk->hold_access = SLA_HOLD_OPEN;
07061          else {
07062             ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
07063                var->value, trunk->name);
07064          }
07065       } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
07066          ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
07067             var->name, var->lineno, SLA_CONFIG_FILE);
07068       }
07069    }
07070 
07071    ao2_unlock(trunk);
07072 
07073    if (!ast_strlen_zero(trunk->autocontext)) {
07074       struct ast_context *context;
07075       context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
07076       if (!context) {
07077          ast_log(LOG_ERROR, "Failed to automatically find or create "
07078             "context '%s' for SLA!\n", trunk->autocontext);
07079          return -1;
07080       }
07081       if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
07082          NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
07083          ast_log(LOG_ERROR, "Failed to automatically create extension "
07084             "for trunk '%s'!\n", trunk->name);
07085          return -1;
07086       }
07087    }
07088 
07089    if (!existing_trunk) {
07090       ao2_link(sla_trunks, trunk);
07091    }
07092 
07093    return 0;
07094 }
07095 
07096 /*!
07097  * \internal
07098  * \pre station is not locked
07099  */
07100 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
07101 {
07102    RAII_VAR(struct sla_trunk *, trunk, NULL, unref_obj);
07103    struct sla_trunk_ref *trunk_ref = NULL;
07104    struct sla_station_ref *station_ref;
07105    char *trunk_name, *options, *cur;
07106    int existing_trunk_ref = 0;
07107    int existing_station_ref = 0;
07108 
07109    options = ast_strdupa(var->value);
07110    trunk_name = strsep(&options, ",");
07111 
07112    trunk = sla_find_trunk(trunk_name);
07113    if (!trunk) {
07114       ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
07115       return;
07116    }
07117 
07118    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07119       if (trunk_ref->trunk == trunk) {
07120          trunk_ref->mark = 0;
07121          existing_trunk_ref = 1;
07122          break;
07123       }
07124    }
07125 
07126    if (!trunk_ref && !(trunk_ref = create_trunk_ref(trunk))) {
07127       return;
07128    }
07129 
07130    trunk_ref->state = SLA_TRUNK_STATE_IDLE;
07131 
07132    while ((cur = strsep(&options, ","))) {
07133       char *name, *value = cur;
07134       name = strsep(&value, "=");
07135       if (!strcasecmp(name, "ringtimeout")) {
07136          if (sscanf(value, "%30u", &trunk_ref->ring_timeout) != 1) {
07137             ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
07138                "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
07139             trunk_ref->ring_timeout = 0;
07140          }
07141       } else if (!strcasecmp(name, "ringdelay")) {
07142          if (sscanf(value, "%30u", &trunk_ref->ring_delay) != 1) {
07143             ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
07144                "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
07145             trunk_ref->ring_delay = 0;
07146          }
07147       } else {
07148          ast_log(LOG_WARNING, "Invalid option '%s' for "
07149             "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
07150       }
07151    }
07152 
07153    AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
07154       if (station_ref->station == station) {
07155          station_ref->mark = 0;
07156          existing_station_ref = 1;
07157          break;
07158       }
07159    }
07160 
07161    if (!station_ref && !(station_ref = sla_create_station_ref(station))) {
07162       if (!existing_trunk_ref) {
07163          ao2_ref(trunk_ref, -1);
07164       } else {
07165          trunk_ref->mark = 1;
07166       }
07167       return;
07168    }
07169 
07170    if (!existing_station_ref) {
07171       ao2_lock(trunk);
07172       AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
07173       ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
07174       ao2_unlock(trunk);
07175    }
07176 
07177    if (!existing_trunk_ref) {
07178       ao2_lock(station);
07179       AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
07180       ao2_unlock(station);
07181    }
07182 }
07183 
07184 static int sla_build_station(struct ast_config *cfg, const char *cat)
07185 {
07186    RAII_VAR(struct sla_station *, station, NULL, unref_obj);
07187    struct ast_variable *var;
07188    const char *dev;
07189    int existing_station = 0;
07190 
07191    if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
07192       ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
07193       return -1;
07194    }
07195 
07196    if ((station = sla_find_station(cat))) {
07197       station->mark = 0;
07198       existing_station = 1;
07199    } else if ((station = ao2_alloc(sizeof(*station), sla_station_destructor))) {
07200       if (ast_string_field_init(station, 32)) {
07201          return -1;
07202       }
07203       ast_string_field_set(station, name, cat);
07204    } else {
07205       return -1;
07206    }
07207 
07208    ao2_lock(station);
07209 
07210    ast_string_field_set(station, device, dev);
07211 
07212    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
07213       if (!strcasecmp(var->name, "trunk")) {
07214          ao2_unlock(station);
07215          sla_add_trunk_to_station(station, var);
07216          ao2_lock(station);
07217       } else if (!strcasecmp(var->name, "autocontext")) {
07218          ast_string_field_set(station, autocontext, var->value);
07219       } else if (!strcasecmp(var->name, "ringtimeout")) {
07220          if (sscanf(var->value, "%30u", &station->ring_timeout) != 1) {
07221             ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
07222                var->value, station->name);
07223             station->ring_timeout = 0;
07224          }
07225       } else if (!strcasecmp(var->name, "ringdelay")) {
07226          if (sscanf(var->value, "%30u", &station->ring_delay) != 1) {
07227             ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
07228                var->value, station->name);
07229             station->ring_delay = 0;
07230          }
07231       } else if (!strcasecmp(var->name, "hold")) {
07232          if (!strcasecmp(var->value, "private"))
07233             station->hold_access = SLA_HOLD_PRIVATE;
07234          else if (!strcasecmp(var->value, "open"))
07235             station->hold_access = SLA_HOLD_OPEN;
07236          else {
07237             ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
07238                var->value, station->name);
07239          }
07240 
07241       } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
07242          ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
07243             var->name, var->lineno, SLA_CONFIG_FILE);
07244       }
07245    }
07246 
07247    ao2_unlock(station);
07248 
07249    if (!ast_strlen_zero(station->autocontext)) {
07250       struct ast_context *context;
07251       struct sla_trunk_ref *trunk_ref;
07252       context = ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar);
07253       if (!context) {
07254          ast_log(LOG_ERROR, "Failed to automatically find or create "
07255             "context '%s' for SLA!\n", station->autocontext);
07256          return -1;
07257       }
07258       /* The extension for when the handset goes off-hook.
07259        * exten => station1,1,SLAStation(station1) */
07260       if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
07261          NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
07262          ast_log(LOG_ERROR, "Failed to automatically create extension "
07263             "for trunk '%s'!\n", station->name);
07264          return -1;
07265       }
07266       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07267          char exten[AST_MAX_EXTENSION];
07268          char hint[AST_MAX_APP];
07269          snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
07270          snprintf(hint, sizeof(hint), "SLA:%s", exten);
07271          /* Extension for this line button 
07272           * exten => station1_line1,1,SLAStation(station1_line1) */
07273          if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
07274             NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
07275             ast_log(LOG_ERROR, "Failed to automatically create extension "
07276                "for trunk '%s'!\n", station->name);
07277             return -1;
07278          }
07279          /* Hint for this line button 
07280           * exten => station1_line1,hint,SLA:station1_line1 */
07281          if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
07282             NULL, NULL, hint, NULL, NULL, sla_registrar)) {
07283             ast_log(LOG_ERROR, "Failed to automatically create hint "
07284                "for trunk '%s'!\n", station->name);
07285             return -1;
07286          }
07287       }
07288    }
07289 
07290    if (!existing_station) {
07291       ao2_link(sla_stations, station);
07292    }
07293 
07294    return 0;
07295 }
07296 
07297 static int sla_trunk_mark(void *obj, void *arg, int flags)
07298 {
07299    struct sla_trunk *trunk = obj;
07300    struct sla_station_ref *station_ref;
07301 
07302    ao2_lock(trunk);
07303 
07304    trunk->mark = 1;
07305 
07306    AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
07307       station_ref->mark = 1;
07308    }
07309 
07310    ao2_unlock(trunk);
07311 
07312    return 0;
07313 }
07314 
07315 static int sla_station_mark(void *obj, void *arg, int flags)
07316 {
07317    struct sla_station *station = obj;
07318    struct sla_trunk_ref *trunk_ref;
07319 
07320    ao2_lock(station);
07321 
07322    station->mark = 1;
07323 
07324    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07325       trunk_ref->mark = 1;
07326    }
07327 
07328    ao2_unlock(station);
07329 
07330    return 0;
07331 }
07332 
07333 static int sla_trunk_is_marked(void *obj, void *arg, int flags)
07334 {
07335    struct sla_trunk *trunk = obj;
07336 
07337    ao2_lock(trunk);
07338 
07339    if (trunk->mark) {
07340       /* Only remove all of the station references if the trunk itself is going away */
07341       sla_trunk_release_refs(trunk, NULL, 0);
07342    } else {
07343       struct sla_station_ref *station_ref;
07344 
07345       /* Otherwise only remove references to stations no longer in the config */
07346       AST_LIST_TRAVERSE_SAFE_BEGIN(&trunk->stations, station_ref, entry) {
07347          if (!station_ref->mark) {
07348             continue;
07349          }
07350          AST_LIST_REMOVE_CURRENT(entry);
07351          ao2_ref(station_ref, -1);
07352       }
07353       AST_LIST_TRAVERSE_SAFE_END
07354    }
07355 
07356    ao2_unlock(trunk);
07357 
07358    return trunk->mark ? CMP_MATCH : 0;
07359 }
07360 
07361 static int sla_station_is_marked(void *obj, void *arg, int flags)
07362 {
07363    struct sla_station *station = obj;
07364 
07365    ao2_lock(station);
07366 
07367    if (station->mark) {
07368       /* Only remove all of the trunk references if the station itself is going away */
07369       sla_station_release_refs(station, NULL, 0);
07370    } else {
07371       struct sla_trunk_ref *trunk_ref;
07372 
07373       /* Otherwise only remove references to trunks no longer in the config */
07374       AST_LIST_TRAVERSE_SAFE_BEGIN(&station->trunks, trunk_ref, entry) {
07375          if (!trunk_ref->mark) {
07376             continue;
07377          }
07378          AST_LIST_REMOVE_CURRENT(entry);
07379          ao2_ref(trunk_ref, -1);
07380       }
07381       AST_LIST_TRAVERSE_SAFE_END
07382    }
07383 
07384    ao2_unlock(station);
07385 
07386    return station->mark ? CMP_MATCH : 0;
07387 }
07388 
07389 static int sla_in_use(void)
07390 {
07391    return ao2_container_count(sla_trunks) || ao2_container_count(sla_stations);
07392 }
07393 
07394 static int sla_load_config(int reload)
07395 {
07396    struct ast_config *cfg;
07397    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
07398    const char *cat = NULL;
07399    int res = 0;
07400    const char *val;
07401 
07402    if (!reload) {
07403       ast_mutex_init(&sla.lock);
07404       ast_cond_init(&sla.cond, NULL);
07405       sla_trunks = ao2_container_alloc(1, sla_trunk_hash, sla_trunk_cmp);
07406       sla_stations = ao2_container_alloc(1, sla_station_hash, sla_station_cmp);
07407    }
07408 
07409    if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags))) {
07410       return 0; /* Treat no config as normal */
07411    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
07412       return 0;
07413    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
07414       ast_log(LOG_ERROR, "Config file " SLA_CONFIG_FILE " is in an invalid format.  Aborting.\n");
07415       return 0;
07416    }
07417 
07418    if (reload) {
07419       ao2_callback(sla_trunks, 0, sla_trunk_mark, NULL);
07420       ao2_callback(sla_stations, 0, sla_station_mark, NULL);
07421    }
07422 
07423    if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
07424       sla.attempt_callerid = ast_true(val);
07425 
07426    while ((cat = ast_category_browse(cfg, cat)) && !res) {
07427       const char *type;
07428       if (!strcasecmp(cat, "general"))
07429          continue;
07430       if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
07431          ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
07432             SLA_CONFIG_FILE);
07433          continue;
07434       }
07435       if (!strcasecmp(type, "trunk"))
07436          res = sla_build_trunk(cfg, cat);
07437       else if (!strcasecmp(type, "station"))
07438          res = sla_build_station(cfg, cat);
07439       else {
07440          ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
07441             SLA_CONFIG_FILE, type);
07442       }
07443    }
07444 
07445    ast_config_destroy(cfg);
07446 
07447    if (reload) {
07448       ao2_callback(sla_trunks, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_trunk_is_marked, NULL);
07449       ao2_callback(sla_stations, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_station_is_marked, NULL);
07450    }
07451 
07452    /* Start SLA event processing thread once SLA has been configured. */
07453    if (sla.thread == AST_PTHREADT_NULL && sla_in_use()) {
07454       ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
07455    }
07456 
07457    return res;
07458 }
07459 
07460 static int acf_meetme_info_eval(const char *keyword, const struct ast_conference *conf)
07461 {
07462    if (!strcasecmp("lock", keyword)) {
07463       return conf->locked;
07464    } else if (!strcasecmp("parties", keyword)) {
07465       return conf->users;
07466    } else if (!strcasecmp("activity", keyword)) {
07467       time_t now;
07468       now = time(NULL);
07469       return (now - conf->start);
07470    } else if (!strcasecmp("dynamic", keyword)) {
07471       return conf->isdynamic;
07472    } else {
07473       return -1;
07474    }
07475 
07476 }
07477 
07478 static int acf_meetme_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
07479 {
07480    struct ast_conference *conf;
07481    char *parse;
07482    int result = -2; /* only non-negative numbers valid, -1 is used elsewhere */
07483    AST_DECLARE_APP_ARGS(args,
07484       AST_APP_ARG(keyword);
07485       AST_APP_ARG(confno);
07486    );
07487 
07488    if (ast_strlen_zero(data)) {
07489       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires two arguments\n");
07490       return -1;
07491    }
07492 
07493    parse = ast_strdupa(data);
07494    AST_STANDARD_APP_ARGS(args, parse);
07495 
07496    if (ast_strlen_zero(args.keyword)) {
07497       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a keyword\n");
07498       return -1;
07499    }
07500 
07501    if (ast_strlen_zero(args.confno)) {
07502       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a conference number\n");
07503       return -1;
07504    }
07505 
07506    AST_LIST_LOCK(&confs);
07507    AST_LIST_TRAVERSE(&confs, conf, list) {
07508       if (!strcmp(args.confno, conf->confno)) {
07509          result = acf_meetme_info_eval(args.keyword, conf);
07510          break;
07511       }
07512    }
07513    AST_LIST_UNLOCK(&confs);
07514 
07515    if (result > -1) {
07516       snprintf(buf, len, "%d", result);
07517    } else if (result == -1) {
07518       ast_log(LOG_NOTICE, "Error: invalid keyword: '%s'\n", args.keyword);
07519       snprintf(buf, len, "0");
07520    } else if (result == -2) {
07521       ast_log(LOG_NOTICE, "Error: conference (%s) not found\n", args.confno); 
07522       snprintf(buf, len, "0");
07523    }
07524 
07525    return 0;
07526 }
07527 
07528 
07529 static struct ast_custom_function meetme_info_acf = {
07530    .name = "MEETME_INFO",
07531    .read = acf_meetme_info,
07532 };
07533 
07534 
07535 static int load_config(int reload)
07536 {
07537    load_config_meetme();
07538    return sla_load_config(reload);
07539 }
07540 
07541 #define MEETME_DATA_EXPORT(MEMBER)              \
07542    MEMBER(ast_conference, confno, AST_DATA_STRING)       \
07543    MEMBER(ast_conference, dahdiconf, AST_DATA_INTEGER)      \
07544    MEMBER(ast_conference, users, AST_DATA_INTEGER)       \
07545    MEMBER(ast_conference, markedusers, AST_DATA_INTEGER)    \
07546    MEMBER(ast_conference, maxusers, AST_DATA_INTEGER)    \
07547    MEMBER(ast_conference, isdynamic, AST_DATA_BOOLEAN)      \
07548    MEMBER(ast_conference, locked, AST_DATA_BOOLEAN)      \
07549    MEMBER(ast_conference, recordingfilename, AST_DATA_STRING)  \
07550    MEMBER(ast_conference, recordingformat, AST_DATA_STRING) \
07551    MEMBER(ast_conference, pin, AST_DATA_PASSWORD)        \
07552    MEMBER(ast_conference, pinadmin, AST_DATA_PASSWORD)      \
07553    MEMBER(ast_conference, start, AST_DATA_TIMESTAMP)     \
07554    MEMBER(ast_conference, endtime, AST_DATA_TIMESTAMP)
07555 
07556 AST_DATA_STRUCTURE(ast_conference, MEETME_DATA_EXPORT);
07557 
07558 #define MEETME_USER_DATA_EXPORT(MEMBER)               \
07559    MEMBER(ast_conf_user, user_no, AST_DATA_INTEGER)      \
07560    MEMBER(ast_conf_user, talking, AST_DATA_BOOLEAN)      \
07561    MEMBER(ast_conf_user, dahdichannel, AST_DATA_BOOLEAN)    \
07562    MEMBER(ast_conf_user, jointime, AST_DATA_TIMESTAMP)      \
07563    MEMBER(ast_conf_user, kicktime, AST_DATA_TIMESTAMP)      \
07564    MEMBER(ast_conf_user, timelimit, AST_DATA_MILLISECONDS)     \
07565    MEMBER(ast_conf_user, play_warning, AST_DATA_MILLISECONDS)  \
07566    MEMBER(ast_conf_user, warning_freq, AST_DATA_MILLISECONDS)
07567 
07568 AST_DATA_STRUCTURE(ast_conf_user, MEETME_USER_DATA_EXPORT);
07569 
07570 static int user_add_provider_cb(void *obj, void *arg, int flags)
07571 {
07572    struct ast_data *data_meetme_user;
07573    struct ast_data *data_meetme_user_channel;
07574    struct ast_data *data_meetme_user_volume;
07575 
07576    struct ast_conf_user *user = obj;
07577    struct ast_data *data_meetme_users = arg;
07578 
07579    data_meetme_user = ast_data_add_node(data_meetme_users, "user");
07580    if (!data_meetme_user) {
07581       return 0;
07582    }
07583    /* user structure */
07584    ast_data_add_structure(ast_conf_user, data_meetme_user, user);
07585 
07586    /* user's channel */
07587    data_meetme_user_channel = ast_data_add_node(data_meetme_user, "channel");
07588    if (!data_meetme_user_channel) {
07589       return 0;
07590    }
07591 
07592    ast_channel_data_add_structure(data_meetme_user_channel, user->chan, 1);
07593 
07594    /* volume structure */
07595    data_meetme_user_volume = ast_data_add_node(data_meetme_user, "listen-volume");
07596    if (!data_meetme_user_volume) {
07597       return 0;
07598    }
07599    ast_data_add_int(data_meetme_user_volume, "desired", user->listen.desired);
07600    ast_data_add_int(data_meetme_user_volume, "actual", user->listen.actual);
07601 
07602    data_meetme_user_volume = ast_data_add_node(data_meetme_user, "talk-volume");
07603    if (!data_meetme_user_volume) {
07604       return 0;
07605    }
07606    ast_data_add_int(data_meetme_user_volume, "desired", user->talk.desired);
07607    ast_data_add_int(data_meetme_user_volume, "actual", user->talk.actual);
07608 
07609    return 0;
07610 }
07611 
07612 /*!
07613  * \internal
07614  * \brief Implements the meetme data provider.
07615  */
07616 static int meetme_data_provider_get(const struct ast_data_search *search,
07617    struct ast_data *data_root)
07618 {
07619    struct ast_conference *cnf;
07620    struct ast_data *data_meetme, *data_meetme_users;
07621 
07622    AST_LIST_LOCK(&confs);
07623    AST_LIST_TRAVERSE(&confs, cnf, list) {
07624       data_meetme = ast_data_add_node(data_root, "meetme");
07625       if (!data_meetme) {
07626          continue;
07627       }
07628 
07629       ast_data_add_structure(ast_conference, data_meetme, cnf);
07630 
07631       if (ao2_container_count(cnf->usercontainer)) {
07632          data_meetme_users = ast_data_add_node(data_meetme, "users");
07633          if (!data_meetme_users) {
07634             ast_data_remove_node(data_root, data_meetme);
07635             continue;
07636          }
07637 
07638          ao2_callback(cnf->usercontainer, OBJ_NODATA, user_add_provider_cb, data_meetme_users); 
07639       }
07640 
07641       if (!ast_data_search_match(search, data_meetme)) {
07642          ast_data_remove_node(data_root, data_meetme);
07643       }
07644    }
07645    AST_LIST_UNLOCK(&confs);
07646 
07647    return 0;
07648 }
07649 
07650 static const struct ast_data_handler meetme_data_provider = {
07651    .version = AST_DATA_HANDLER_VERSION,
07652    .get = meetme_data_provider_get
07653 };
07654 
07655 static const struct ast_data_entry meetme_data_providers[] = {
07656    AST_DATA_ENTRY("asterisk/application/meetme/list", &meetme_data_provider),
07657 };
07658 
07659 #ifdef TEST_FRAMEWORK
07660 AST_TEST_DEFINE(test_meetme_data_provider)
07661 {
07662    struct ast_channel *chan;
07663    struct ast_conference *cnf;
07664    struct ast_data *node;
07665    struct ast_data_query query = {
07666       .path = "/asterisk/application/meetme/list",
07667       .search = "list/meetme/confno=9898"
07668    };
07669 
07670    switch (cmd) {
07671    case TEST_INIT:
07672       info->name = "meetme_get_data_test";
07673       info->category = "/main/data/app_meetme/list/";
07674       info->summary = "Meetme data provider unit test";
07675       info->description =
07676          "Tests whether the Meetme data provider implementation works as expected.";
07677       return AST_TEST_NOT_RUN;
07678    case TEST_EXECUTE:
07679       break;
07680    }
07681 
07682    chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, 0, 0, "MeetMeTest");
07683    if (!chan) {
07684       ast_test_status_update(test, "Channel allocation failed\n");
07685       return AST_TEST_FAIL;
07686    }
07687 
07688    cnf = build_conf("9898", "", "1234", 1, 1, 1, chan, test);
07689    if (!cnf) {
07690       ast_test_status_update(test, "Build of test conference 9898 failed\n");
07691       ast_hangup(chan);
07692       return AST_TEST_FAIL;
07693    }
07694 
07695    node = ast_data_get(&query);
07696    if (!node) {
07697       ast_test_status_update(test, "Data query for test conference 9898 failed\n");
07698       dispose_conf(cnf);
07699       ast_hangup(chan);
07700       return AST_TEST_FAIL;
07701    }
07702 
07703    if (strcmp(ast_data_retrieve_string(node, "meetme/confno"), "9898")) {
07704       ast_test_status_update(test, "Query returned the wrong conference\n");
07705       dispose_conf(cnf);
07706       ast_hangup(chan);
07707       ast_data_free(node);
07708       return AST_TEST_FAIL;
07709    }
07710 
07711    ast_data_free(node);
07712    dispose_conf(cnf);
07713    ast_hangup(chan);
07714 
07715    return AST_TEST_PASS;
07716 }
07717 #endif
07718 
07719 static int unload_module(void)
07720 {
07721    int res = 0;
07722    
07723    ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
07724    res = ast_manager_unregister("MeetmeMute");
07725    res |= ast_manager_unregister("MeetmeUnmute");
07726    res |= ast_manager_unregister("MeetmeList");
07727    res |= ast_unregister_application(app4);
07728    res |= ast_unregister_application(app3);
07729    res |= ast_unregister_application(app2);
07730    res |= ast_unregister_application(app);
07731    res |= ast_unregister_application(slastation_app);
07732    res |= ast_unregister_application(slatrunk_app);
07733 
07734 #ifdef TEST_FRAMEWORK
07735    AST_TEST_UNREGISTER(test_meetme_data_provider);
07736 #endif
07737    ast_data_unregister(NULL);
07738 
07739    ast_devstate_prov_del("Meetme");
07740    ast_devstate_prov_del("SLA");
07741    
07742    sla_destroy();
07743    
07744    res |= ast_custom_function_unregister(&meetme_info_acf);
07745    ast_unload_realtime("meetme");
07746 
07747    return res;
07748 }
07749 
07750 static int load_module(void)
07751 {
07752    int res = 0;
07753 
07754    res |= load_config(0);
07755 
07756    ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
07757    res |= ast_manager_register_xml("MeetmeMute", EVENT_FLAG_CALL, action_meetmemute);
07758    res |= ast_manager_register_xml("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute);
07759    res |= ast_manager_register_xml("MeetmeList", EVENT_FLAG_REPORTING, action_meetmelist);
07760    res |= ast_register_application_xml(app4, channel_admin_exec);
07761    res |= ast_register_application_xml(app3, admin_exec);
07762    res |= ast_register_application_xml(app2, count_exec);
07763    res |= ast_register_application_xml(app, conf_exec);
07764    res |= ast_register_application_xml(slastation_app, sla_station_exec);
07765    res |= ast_register_application_xml(slatrunk_app, sla_trunk_exec);
07766 
07767 #ifdef TEST_FRAMEWORK
07768    AST_TEST_REGISTER(test_meetme_data_provider);
07769 #endif
07770    ast_data_register_multiple(meetme_data_providers, ARRAY_LEN(meetme_data_providers));
07771 
07772    res |= ast_devstate_prov_add("Meetme", meetmestate);
07773    res |= ast_devstate_prov_add("SLA", sla_state);
07774 
07775    res |= ast_custom_function_register(&meetme_info_acf);
07776    ast_realtime_require_field("meetme", "confno", RQ_UINTEGER2, 3, "members", RQ_UINTEGER1, 3, NULL);
07777 
07778    return res;
07779 }
07780 
07781 static int reload(void)
07782 {
07783    ast_unload_realtime("meetme");
07784    return load_config(1);
07785 }
07786 
07787 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "MeetMe conference bridge",
07788       .load = load_module,
07789       .unload = unload_module,
07790       .reload = reload,
07791       .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
07792           );
07793 

Generated on 7 Sep 2017 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1