Sat Aug 6 00:39:20 2011

Asterisk developer's documentation


app_meetme.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2007, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * SLA Implementation by:
00009  * Russell Bryant <russell@digium.com>
00010  *
00011  * See http://www.asterisk.org for more information about
00012  * the Asterisk project. Please do not directly contact
00013  * any of the maintainers of this project for assistance;
00014  * the project provides a web site, mailing lists and IRC
00015  * channels for your use.
00016  *
00017  * This program is free software, distributed under the terms of
00018  * the GNU General Public License Version 2. See the LICENSE file
00019  * at the top of the source tree.
00020  */
00021 
00022 /*! \file
00023  *
00024  * \brief Meet me conference bridge and Shared Line Appearances
00025  *
00026  * \author Mark Spencer <markster@digium.com>
00027  * \author (SLA) Russell Bryant <russell@digium.com>
00028  * 
00029  * \ingroup applications
00030  */
00031 
00032 /*** MODULEINFO
00033    <depend>dahdi</depend>
00034  ***/
00035 
00036 #include "asterisk.h"
00037 
00038 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 320235 $")
00039 
00040 #include <stdlib.h>
00041 #include <stdio.h>
00042 #include <string.h>
00043 #include <unistd.h>
00044 #include <errno.h>
00045 #include <sys/ioctl.h>
00046 #include <sys/stat.h>
00047 #include <sys/types.h>
00048 
00049 #include "asterisk/lock.h"
00050 #include "asterisk/file.h"
00051 #include "asterisk/logger.h"
00052 #include "asterisk/channel.h"
00053 #include "asterisk/pbx.h"
00054 #include "asterisk/module.h"
00055 #include "asterisk/config.h"
00056 #include "asterisk/app.h"
00057 #include "asterisk/dsp.h"
00058 #include "asterisk/musiconhold.h"
00059 #include "asterisk/manager.h"
00060 #include "asterisk/options.h"
00061 #include "asterisk/cli.h"
00062 #include "asterisk/say.h"
00063 #include "asterisk/utils.h"
00064 #include "asterisk/translate.h"
00065 #include "asterisk/ulaw.h"
00066 #include "asterisk/astobj.h"
00067 #include "asterisk/astobj2.h"
00068 #include "asterisk/devicestate.h"
00069 #include "asterisk/dial.h"
00070 #include "asterisk/causes.h"
00071 
00072 #include "asterisk/dahdi_compat.h"
00073 
00074 #include "enter.h"
00075 #include "leave.h"
00076 
00077 #define CONFIG_FILE_NAME "meetme.conf"
00078 #define SLA_CONFIG_FILE  "sla.conf"
00079 
00080 /*! each buffer is 20ms, so this is 640ms total */
00081 #define DEFAULT_AUDIO_BUFFERS  32
00082 
00083 enum {
00084    ADMINFLAG_MUTED =     (1 << 1), /*!< User is muted */
00085    ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
00086    ADMINFLAG_KICKME =    (1 << 3)  /*!< User has been kicked */
00087 };
00088 
00089 #define MEETME_DELAYDETECTTALK     300
00090 #define MEETME_DELAYDETECTENDTALK  1000
00091 
00092 #define AST_FRAME_BITS  32
00093 
00094 enum volume_action {
00095    VOL_UP,
00096    VOL_DOWN
00097 };
00098 
00099 enum entrance_sound {
00100    ENTER,
00101    LEAVE
00102 };
00103 
00104 enum recording_state {
00105    MEETME_RECORD_OFF,
00106    MEETME_RECORD_STARTED,
00107    MEETME_RECORD_ACTIVE,
00108    MEETME_RECORD_TERMINATE
00109 };
00110 
00111 #define CONF_SIZE  320
00112 
00113 enum {
00114    /*! user has admin access on the conference */
00115    CONFFLAG_ADMIN = (1 << 0),
00116    /*! If set the user can only receive audio from the conference */
00117    CONFFLAG_MONITOR = (1 << 1),
00118    /*! If set asterisk will exit conference when '#' is pressed */
00119    CONFFLAG_POUNDEXIT = (1 << 2),
00120    /*! If set asterisk will provide a menu to the user when '*' is pressed */
00121    CONFFLAG_STARMENU = (1 << 3),
00122    /*! If set the use can only send audio to the conference */
00123    CONFFLAG_TALKER = (1 << 4),
00124    /*! If set there will be no enter or leave sounds */
00125    CONFFLAG_QUIET = (1 << 5),
00126    /*! If set, when user joins the conference, they will be told the number 
00127     *  of users that are already in */
00128    CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
00129    /*! Set to run AGI Script in Background */
00130    CONFFLAG_AGI = (1 << 7),
00131    /*! Set to have music on hold when user is alone in conference */
00132    CONFFLAG_MOH = (1 << 8),
00133    /*! If set the MeetMe will return if all marked with this flag left */
00134    CONFFLAG_MARKEDEXIT = (1 << 9),
00135    /*! If set, the MeetMe will wait until a marked user enters */
00136    CONFFLAG_WAITMARKED = (1 << 10),
00137    /*! If set, the MeetMe will exit to the specified context */
00138    CONFFLAG_EXIT_CONTEXT = (1 << 11),
00139    /*! If set, the user will be marked */
00140    CONFFLAG_MARKEDUSER = (1 << 12),
00141    /*! If set, user will be ask record name on entry of conference */
00142    CONFFLAG_INTROUSER = (1 << 13),
00143    /*! If set, the MeetMe will be recorded */
00144    CONFFLAG_RECORDCONF = (1<< 14),
00145    /*! If set, the user will be monitored if the user is talking or not */
00146    CONFFLAG_MONITORTALKER = (1 << 15),
00147    CONFFLAG_DYNAMIC = (1 << 16),
00148    CONFFLAG_DYNAMICPIN = (1 << 17),
00149    CONFFLAG_EMPTY = (1 << 18),
00150    CONFFLAG_EMPTYNOPIN = (1 << 19),
00151    CONFFLAG_ALWAYSPROMPT = (1 << 20),
00152    /*! If set, treats talking users as muted users */
00153    CONFFLAG_OPTIMIZETALKER = (1 << 21),
00154    /*! If set, won't speak the extra prompt when the first person 
00155     *  enters the conference */
00156    CONFFLAG_NOONLYPERSON = (1 << 22),
00157    /*! If set, user will be asked to record name on entry of conference 
00158     *  without review */
00159    CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
00160    /*! If set, the user will be initially self-muted */
00161    CONFFLAG_STARTMUTED = (1 << 24),
00162    /*! Pass DTMF through the conference */
00163    CONFFLAG_PASS_DTMF = (1 << 25),
00164    /*! This is a SLA station. (Only for use by the SLA applications.) */
00165    CONFFLAG_SLA_STATION = (1 << 26),
00166    /*! This is a SLA trunk. (Only for use by the SLA applications.) */
00167    CONFFLAG_SLA_TRUNK = (1 << 27),
00168    /*! Do not write any audio to this channel until the state is up. */
00169    CONFFLAG_NO_AUDIO_UNTIL_UP = (1 << 28),
00170    /*! Play an arbitrary Intro message */
00171    CONFFLAG_INTROMSG = (1 << 29),
00172 };
00173 
00174 enum {
00175    OPT_ARG_WAITMARKED = 0,
00176    OPT_ARG_INTROMSG = 1,
00177    OPT_ARG_ARRAY_SIZE = 2,
00178 };
00179 
00180 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
00181    AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
00182    AST_APP_OPTION('a', CONFFLAG_ADMIN ),
00183    AST_APP_OPTION('b', CONFFLAG_AGI ),
00184    AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
00185    AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
00186    AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
00187    AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
00188    AST_APP_OPTION('e', CONFFLAG_EMPTY ),
00189    AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
00190    AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
00191    AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
00192    AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
00193    AST_APP_OPTION('M', CONFFLAG_MOH ),
00194    AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
00195    AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
00196    AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
00197    AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ),
00198    AST_APP_OPTION('q', CONFFLAG_QUIET ),
00199    AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
00200    AST_APP_OPTION('s', CONFFLAG_STARMENU ),
00201    AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
00202    AST_APP_OPTION('l', CONFFLAG_MONITOR ),
00203    AST_APP_OPTION('t', CONFFLAG_TALKER ),
00204    AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
00205    AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
00206    AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
00207    AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
00208 END_OPTIONS );
00209 
00210 static const char *app = "MeetMe";
00211 static const char *app2 = "MeetMeCount";
00212 static const char *app3 = "MeetMeAdmin";
00213 static const char *slastation_app = "SLAStation";
00214 static const char *slatrunk_app = "SLATrunk";
00215 
00216 static const char *synopsis = "MeetMe conference bridge";
00217 static const char *synopsis2 = "MeetMe participant count";
00218 static const char *synopsis3 = "MeetMe conference Administration";
00219 static const char *slastation_synopsis = "Shared Line Appearance Station";
00220 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
00221 
00222 static const char *descrip =
00223 "  MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
00224 "conference.  If the conference number is omitted, the user will be prompted\n"
00225 "to enter one.  User can exit the conference by hangup, or if the 'p' option\n"
00226 "is specified, by pressing '#'.\n"
00227 "Please note: The DAHDI kernel modules and at least one hardware driver (or dahdi_dummy)\n"
00228 "             must be present for conferencing to operate properly. In addition, the chan_dahdi\n"
00229 "             channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
00230 "The option string may contain zero or more of the following characters:\n"
00231 "      'a' -- set admin mode\n"
00232 "      'A' -- set marked mode\n"
00233 "      'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
00234 "             Default: conf-background.agi  (Note: This does not work with\n"
00235 "             non-DAHDI channels in the same conference)\n"
00236 "      'c' -- announce user(s) count on joining a conference\n"
00237 "      'd' -- dynamically add conference\n"
00238 "      'D' -- dynamically add conference, prompting for a PIN\n"
00239 "      'e' -- select an empty conference\n"
00240 "      'E' -- select an empty pinless conference\n"
00241 "      'F' -- Pass DTMF through the conference.\n"
00242 "      'G(x)'\n"
00243 "          -- Play an announcement in conference, using 'x' as the file\n"
00244 "      'i' -- announce user join/leave with review\n"
00245 "      'I' -- announce user join/leave without review\n"
00246 "      'l' -- set listen only mode (Listen only, no talking)\n"
00247 "      'm' -- set initially muted\n"
00248 "      'M' -- enable music on hold when the conference has a single caller\n"
00249 "      'o' -- set talker optimization - treats talkers who aren't speaking as\n"
00250 "             being muted, meaning (a) No encode is done on transmission and\n"
00251 "             (b) Received audio that is not registered as talking is omitted\n"
00252 "             causing no buildup in background noise.  Note that this option\n"
00253 "             will be removed in 1.6 and enabled by default.\n"
00254 "      'p' -- allow user to exit the conference by pressing '#'\n"
00255 "      'P' -- always prompt for the pin even if it is specified\n"
00256 "      'q' -- quiet mode (don't play enter/leave sounds)\n"
00257 "      'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
00258 "             using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
00259 "             meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
00260 "             wav.\n"
00261 "      's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
00262 "      't' -- set talk only mode. (Talk only, no listening)\n"
00263 "      'T' -- set talker detection (sent to manager interface and meetme list)\n"
00264 "      'w[(<secs>)]'\n"
00265 "          -- wait until the marked user enters the conference\n"
00266 "      'x' -- close the conference when last marked user exits\n"
00267 "      'X' -- allow user to exit the conference by entering a valid single\n"
00268 "             digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
00269 "             if that variable is not defined.\n"
00270 "      '1' -- do not play message when first person enters\n";
00271 
00272 static const char *descrip2 =
00273 "  MeetMeCount(confno[|var]): Plays back the number of users in the specified\n"
00274 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
00275 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
00276 "the channel, unless priority n+1 exists, in which case priority progress will\n"
00277 "continue.\n";
00278 
00279 static const char *descrip3 = 
00280 "  MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
00281 "      'e' -- Eject last user that joined\n"
00282 "      'k' -- Kick one user out of conference\n"
00283 "      'K' -- Kick all users out of conference\n"
00284 "      'l' -- Unlock conference\n"
00285 "      'L' -- Lock conference\n"
00286 "      'm' -- Unmute one user\n"
00287 "      'M' -- Mute one user\n"
00288 "      'n' -- Unmute all users in the conference\n"
00289 "      'N' -- Mute all non-admin users in the conference\n"
00290 "      'r' -- Reset one user's volume settings\n"
00291 "      'R' -- Reset all users volume settings\n"
00292 "      's' -- Lower entire conference speaking volume\n"
00293 "      'S' -- Raise entire conference speaking volume\n"
00294 "      't' -- Lower one user's talk volume\n"
00295 "      'T' -- Raise one user's talk volume\n"
00296 "      'u' -- Lower one user's listen volume\n"
00297 "      'U' -- Raise one user's listen volume\n"
00298 "      'v' -- Lower entire conference listening volume\n"
00299 "      'V' -- Raise entire conference listening volume\n"
00300 "";
00301 
00302 static const char *slastation_desc =
00303 "  SLAStation(station):\n"
00304 "This application should be executed by an SLA station.  The argument depends\n"
00305 "on how the call was initiated.  If the phone was just taken off hook, then\n"
00306 "the argument \"station\" should be just the station name.  If the call was\n"
00307 "initiated by pressing a line key, then the station name should be preceded\n"
00308 "by an underscore and the trunk name associated with that line button.\n"
00309 "For example: \"station1_line1\"."
00310 "  On exit, this application will set the variable SLASTATION_STATUS to\n"
00311 "one of the following values:\n"
00312 "    FAILURE | CONGESTION | SUCCESS\n"
00313 "";
00314 
00315 static const char *slatrunk_desc =
00316 "  SLATrunk(trunk):\n"
00317 "This application should be executed by an SLA trunk on an inbound call.\n"
00318 "The channel calling this application should correspond to the SLA trunk\n"
00319 "with the name \"trunk\" that is being passed as an argument.\n"
00320 "  On exit, this application will set the variable SLATRUNK_STATUS to\n"
00321 "one of the following values:\n"
00322 "   FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n" 
00323 "";
00324 
00325 #define MAX_CONFNUM 80
00326 #define MAX_PIN     80
00327 
00328 /* Enough space for "<conference #>,<pin>,<admin pin>" followed by a 0 byte. */
00329 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
00330 
00331 enum announcetypes {
00332    CONF_HASJOIN,
00333    CONF_HASLEFT
00334 };
00335 
00336 struct announce_listitem {
00337    AST_LIST_ENTRY(announce_listitem) entry;
00338    char namerecloc[PATH_MAX];          /*!< Name Recorded file Location */
00339    char language[MAX_LANGUAGE];
00340    struct ast_channel *confchan;
00341    int confusers;
00342    enum announcetypes announcetype;
00343 };
00344 
00345 /*! \brief The MeetMe Conference object */
00346 struct ast_conference {
00347    ast_mutex_t playlock;                   /*!< Conference specific lock (players) */
00348    ast_mutex_t listenlock;                 /*!< Conference specific lock (listeners) */
00349    char confno[MAX_CONFNUM];               /*!< Conference */
00350    struct ast_channel *chan;               /*!< Announcements channel */
00351    struct ast_channel *lchan;              /*!< Listen/Record channel */
00352    int fd;                                 /*!< Announcements fd */
00353    int zapconf;                            /*!< Zaptel Conf # */
00354    int users;                              /*!< Number of active users */
00355    int markedusers;                        /*!< Number of marked users */
00356    time_t start;                           /*!< Start time (s) */
00357    int refcount;                           /*!< reference count of usage */
00358    enum recording_state recording:2;       /*!< recording status */
00359    unsigned int isdynamic:1;               /*!< Created on the fly? */
00360    unsigned int locked:1;                  /*!< Is the conference locked? */
00361    pthread_t recordthread;                 /*!< thread for recording */
00362    ast_mutex_t recordthreadlock;           /*!< control threads trying to start recordthread */
00363    pthread_attr_t attr;                    /*!< thread attribute */
00364    const char *recordingfilename;          /*!< Filename to record the Conference into */
00365    const char *recordingformat;            /*!< Format to record the Conference in */
00366    char pin[MAX_PIN];                      /*!< If protected by a PIN */
00367    char pinadmin[MAX_PIN];                 /*!< If protected by a admin PIN */
00368    struct ast_frame *transframe[32];
00369    struct ast_frame *origframe;
00370    struct ast_trans_pvt *transpath[32];
00371    struct ao2_container *usercontainer;
00372    AST_LIST_ENTRY(ast_conference) list;
00373    /* announce_thread related data */
00374    pthread_t announcethread;
00375    ast_mutex_t announcethreadlock;
00376    unsigned int announcethread_stop:1;
00377    ast_cond_t announcelist_addition;
00378    AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
00379    ast_mutex_t announcelistlock;
00380 };
00381 
00382 static AST_LIST_HEAD_STATIC(confs, ast_conference);
00383 
00384 static unsigned int conf_map[1024] = {0, };
00385 
00386 struct volume {
00387    int desired;                            /*!< Desired volume adjustment */
00388    int actual;                             /*!< Actual volume adjustment (for channels that can't adjust) */
00389 };
00390 
00391 struct ast_conf_user {
00392    int user_no;                            /*!< User Number */
00393    int userflags;                          /*!< Flags as set in the conference */
00394    int adminflags;                         /*!< Flags set by the Admin */
00395    struct ast_channel *chan;               /*!< Connected channel */
00396    int talking;                            /*!< Is user talking */
00397    int zapchannel;                         /*!< Is a Zaptel channel */
00398    char usrvalue[50];                      /*!< Custom User Value */
00399    char namerecloc[PATH_MAX];          /*!< Name Recorded file Location */
00400    time_t jointime;                        /*!< Time the user joined the conference */
00401    struct volume talk;
00402    struct volume listen;
00403    AST_LIST_ENTRY(ast_conf_user) list;
00404 };
00405 
00406 enum sla_which_trunk_refs {
00407    ALL_TRUNK_REFS,
00408    INACTIVE_TRUNK_REFS,
00409 };
00410 
00411 enum sla_trunk_state {
00412    SLA_TRUNK_STATE_IDLE,
00413    SLA_TRUNK_STATE_RINGING,
00414    SLA_TRUNK_STATE_UP,
00415    SLA_TRUNK_STATE_ONHOLD,
00416    SLA_TRUNK_STATE_ONHOLD_BYME,
00417 };
00418 
00419 enum sla_hold_access {
00420    /*! This means that any station can put it on hold, and any station
00421     * can retrieve the call from hold. */
00422    SLA_HOLD_OPEN,
00423    /*! This means that only the station that put the call on hold may
00424     * retrieve it from hold. */
00425    SLA_HOLD_PRIVATE,
00426 };
00427 
00428 struct sla_trunk_ref;
00429 
00430 struct sla_station {
00431    AST_RWLIST_ENTRY(sla_station) entry;
00432    AST_DECLARE_STRING_FIELDS(
00433       AST_STRING_FIELD(name); 
00434       AST_STRING_FIELD(device);  
00435       AST_STRING_FIELD(autocontext);   
00436    );
00437    AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
00438    struct ast_dial *dial;
00439    /*! Ring timeout for this station, for any trunk.  If a ring timeout
00440     *  is set for a specific trunk on this station, that will take
00441     *  priority over this value. */
00442    unsigned int ring_timeout;
00443    /*! Ring delay for this station, for any trunk.  If a ring delay
00444     *  is set for a specific trunk on this station, that will take
00445     *  priority over this value. */
00446    unsigned int ring_delay;
00447    /*! This option uses the values in the sla_hold_access enum and sets the
00448     * access control type for hold on this station. */
00449    unsigned int hold_access:1;
00450 };
00451 
00452 struct sla_station_ref {
00453    AST_LIST_ENTRY(sla_station_ref) entry;
00454    struct sla_station *station;
00455 };
00456 
00457 struct sla_trunk {
00458    AST_RWLIST_ENTRY(sla_trunk) entry;
00459    AST_DECLARE_STRING_FIELDS(
00460       AST_STRING_FIELD(name);
00461       AST_STRING_FIELD(device);
00462       AST_STRING_FIELD(autocontext);   
00463    );
00464    AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
00465    /*! Number of stations that use this trunk */
00466    unsigned int num_stations;
00467    /*! Number of stations currently on a call with this trunk */
00468    unsigned int active_stations;
00469    /*! Number of stations that have this trunk on hold. */
00470    unsigned int hold_stations;
00471    struct ast_channel *chan;
00472    unsigned int ring_timeout;
00473    /*! If set to 1, no station will be able to join an active call with
00474     *  this trunk. */
00475    unsigned int barge_disabled:1;
00476    /*! This option uses the values in the sla_hold_access enum and sets the
00477     * access control type for hold on this trunk. */
00478    unsigned int hold_access:1;
00479    /*! Whether this trunk is currently on hold, meaning that once a station
00480     *  connects to it, the trunk channel needs to have UNHOLD indicated to it. */
00481    unsigned int on_hold:1;
00482 };
00483 
00484 struct sla_trunk_ref {
00485    AST_LIST_ENTRY(sla_trunk_ref) entry;
00486    struct sla_trunk *trunk;
00487    enum sla_trunk_state state;
00488    struct ast_channel *chan;
00489    /*! Ring timeout to use when this trunk is ringing on this specific
00490     *  station.  This takes higher priority than a ring timeout set at
00491     *  the station level. */
00492    unsigned int ring_timeout;
00493    /*! Ring delay to use when this trunk is ringing on this specific
00494     *  station.  This takes higher priority than a ring delay set at
00495     *  the station level. */
00496    unsigned int ring_delay;
00497 };
00498 
00499 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
00500 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
00501 
00502 static const char sla_registrar[] = "SLA";
00503 
00504 /*! \brief Event types that can be queued up for the SLA thread */
00505 enum sla_event_type {
00506    /*! A station has put the call on hold */
00507    SLA_EVENT_HOLD,
00508    /*! The state of a dial has changed */
00509    SLA_EVENT_DIAL_STATE,
00510    /*! The state of a ringing trunk has changed */
00511    SLA_EVENT_RINGING_TRUNK,
00512 };
00513 
00514 struct sla_event {
00515    enum sla_event_type type;
00516    struct sla_station *station;
00517    struct sla_trunk_ref *trunk_ref;
00518    AST_LIST_ENTRY(sla_event) entry;
00519 };
00520 
00521 /*! \brief A station that failed to be dialed 
00522  * \note Only used by the SLA thread. */
00523 struct sla_failed_station {
00524    struct sla_station *station;
00525    struct timeval last_try;
00526    AST_LIST_ENTRY(sla_failed_station) entry;
00527 };
00528 
00529 /*! \brief A trunk that is ringing */
00530 struct sla_ringing_trunk {
00531    struct sla_trunk *trunk;
00532    /*! The time that this trunk started ringing */
00533    struct timeval ring_begin;
00534    AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
00535    AST_LIST_ENTRY(sla_ringing_trunk) entry;
00536 };
00537 
00538 enum sla_station_hangup {
00539    SLA_STATION_HANGUP_NORMAL,
00540    SLA_STATION_HANGUP_TIMEOUT,
00541 };
00542 
00543 /*! \brief A station that is ringing */
00544 struct sla_ringing_station {
00545    struct sla_station *station;
00546    /*! The time that this station started ringing */
00547    struct timeval ring_begin;
00548    AST_LIST_ENTRY(sla_ringing_station) entry;
00549 };
00550 
00551 /*!
00552  * \brief A structure for data used by the sla thread
00553  */
00554 static struct {
00555    /*! The SLA thread ID */
00556    pthread_t thread;
00557    ast_cond_t cond;
00558    ast_mutex_t lock;
00559    AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
00560    AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
00561    AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
00562    AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
00563    unsigned int stop:1;
00564    /*! Attempt to handle CallerID, even though it is known not to work
00565     *  properly in some situations. */
00566    unsigned int attempt_callerid:1;
00567 } sla = {
00568    .thread = AST_PTHREADT_NULL,
00569 };
00570 
00571 /*! The number of audio buffers to be allocated on pseudo channels
00572  *  when in a conference */
00573 static int audio_buffers;
00574 
00575 /*! Map 'volume' levels from -5 through +5 into
00576  *  decibel (dB) settings for channel drivers
00577  *  Note: these are not a straight linear-to-dB
00578  *  conversion... the numbers have been modified
00579  *  to give the user a better level of adjustability
00580  */
00581 static char const gain_map[] = {
00582    -15,
00583    -13,
00584    -10,
00585    -6,
00586    0,
00587    0,
00588    0,
00589    6,
00590    10,
00591    13,
00592    15,
00593 };
00594 
00595 
00596 static int admin_exec(struct ast_channel *chan, void *data);
00597 static void *recordthread(void *args);
00598 
00599 static char *istalking(int x)
00600 {
00601    if (x > 0)
00602       return "(talking)";
00603    else if (x < 0)
00604       return "(unmonitored)";
00605    else 
00606       return "(not talking)";
00607 }
00608 
00609 static int careful_write(int fd, unsigned char *data, int len, int block)
00610 {
00611    int res;
00612    int x;
00613 
00614    while (len) {
00615       if (block) {
00616          x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
00617          res = ioctl(fd, DAHDI_IOMUX, &x);
00618       } else
00619          res = 0;
00620       if (res >= 0)
00621          res = write(fd, data, len);
00622       if (res < 1) {
00623          if (errno != EAGAIN) {
00624             ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
00625             return -1;
00626          } else
00627             return 0;
00628       }
00629       len -= res;
00630       data += res;
00631    }
00632 
00633    return 0;
00634 }
00635 
00636 static int set_talk_volume(struct ast_conf_user *user, int volume)
00637 {
00638    char gain_adjust;
00639 
00640    /* attempt to make the adjustment in the channel driver;
00641       if successful, don't adjust in the frame reading routine
00642    */
00643    gain_adjust = gain_map[volume + 5];
00644 
00645    return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
00646 }
00647 
00648 static int set_listen_volume(struct ast_conf_user *user, int volume)
00649 {
00650    char gain_adjust;
00651 
00652    /* attempt to make the adjustment in the channel driver;
00653       if successful, don't adjust in the frame reading routine
00654    */
00655    gain_adjust = gain_map[volume + 5];
00656 
00657    return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
00658 }
00659 
00660 static void tweak_volume(struct volume *vol, enum volume_action action)
00661 {
00662    switch (action) {
00663    case VOL_UP:
00664       switch (vol->desired) { 
00665       case 5:
00666          break;
00667       case 0:
00668          vol->desired = 2;
00669          break;
00670       case -2:
00671          vol->desired = 0;
00672          break;
00673       default:
00674          vol->desired++;
00675          break;
00676       }
00677       break;
00678    case VOL_DOWN:
00679       switch (vol->desired) {
00680       case -5:
00681          break;
00682       case 2:
00683          vol->desired = 0;
00684          break;
00685       case 0:
00686          vol->desired = -2;
00687          break;
00688       default:
00689          vol->desired--;
00690          break;
00691       }
00692    }
00693 }
00694 
00695 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
00696 {
00697    tweak_volume(&user->talk, action);
00698    /* attempt to make the adjustment in the channel driver;
00699       if successful, don't adjust in the frame reading routine
00700    */
00701    if (!set_talk_volume(user, user->talk.desired))
00702       user->talk.actual = 0;
00703    else
00704       user->talk.actual = user->talk.desired;
00705 }
00706 
00707 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
00708 {
00709    tweak_volume(&user->listen, action);
00710    /* attempt to make the adjustment in the channel driver;
00711       if successful, don't adjust in the frame reading routine
00712    */
00713    if (!set_listen_volume(user, user->listen.desired))
00714       user->listen.actual = 0;
00715    else
00716       user->listen.actual = user->listen.desired;
00717 }
00718 
00719 static void reset_volumes(struct ast_conf_user *user)
00720 {
00721    signed char zero_volume = 0;
00722 
00723    ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
00724    ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
00725 }
00726 
00727 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
00728 {
00729    unsigned char *data;
00730    int len;
00731    int res = -1;
00732 
00733    if (!chan->_softhangup)
00734       res = ast_autoservice_start(chan);
00735 
00736    AST_LIST_LOCK(&confs);
00737 
00738    switch(sound) {
00739    case ENTER:
00740       data = enter;
00741       len = sizeof(enter);
00742       break;
00743    case LEAVE:
00744       data = leave;
00745       len = sizeof(leave);
00746       break;
00747    default:
00748       data = NULL;
00749       len = 0;
00750    }
00751    if (data) {
00752       careful_write(conf->fd, data, len, 1);
00753    }
00754 
00755    AST_LIST_UNLOCK(&confs);
00756 
00757    if (!res) 
00758       ast_autoservice_stop(chan);
00759 }
00760 
00761 static int user_no_cmp(void *obj, void *arg, int flags)
00762 {
00763    struct ast_conf_user *user = obj;
00764    int *user_no = arg;
00765 
00766    if (user->user_no == *user_no) {
00767       return (CMP_MATCH | CMP_STOP);
00768    }
00769 
00770    return 0;
00771 }
00772 
00773 static int user_max_cmp(void *obj, void *arg, int flags)
00774 {
00775    struct ast_conf_user *user = obj;
00776    int *max_no = arg;
00777 
00778    if (user->user_no > *max_no) {
00779       *max_no = user->user_no;
00780    }
00781 
00782    return 0;
00783 }
00784 
00785 /*!
00786  * \brief Find or create a conference
00787  *
00788  * \param confno The conference name/number
00789  * \param pin The regular user pin
00790  * \param pinadmin The admin pin
00791  * \param make Make the conf if it doesn't exist
00792  * \param dynamic Mark the newly created conference as dynamic
00793  * \param refcount How many references to mark on the conference
00794  *
00795  * \return A pointer to the conference struct, or NULL if it wasn't found and
00796  *         make or dynamic were not set.
00797  */
00798 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount)
00799 {
00800    struct ast_conference *cnf;
00801    struct dahdi_confinfo ztc = { 0, };
00802    int confno_int = 0;
00803 
00804    AST_LIST_LOCK(&confs);
00805 
00806    AST_LIST_TRAVERSE(&confs, cnf, list) {
00807       if (!strcmp(confno, cnf->confno)) 
00808          break;
00809    }
00810 
00811    if (cnf || (!make && !dynamic))
00812       goto cnfout;
00813 
00814    /* Make a new one */
00815    if (!(cnf = ast_calloc(1, sizeof(*cnf))) ||
00816       !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) {
00817       goto cnfout;
00818    }
00819 
00820    ast_mutex_init(&cnf->playlock);
00821    ast_mutex_init(&cnf->listenlock);
00822    cnf->recordthread = AST_PTHREADT_NULL;
00823    ast_mutex_init(&cnf->recordthreadlock);
00824    cnf->announcethread = AST_PTHREADT_NULL;
00825    ast_mutex_init(&cnf->announcethreadlock);
00826    ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
00827    ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
00828    ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
00829 
00830    /* Setup a new zap conference */
00831    ztc.confno = -1;
00832    ztc.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
00833    cnf->fd = open(DAHDI_FILE_PSEUDO, O_RDWR);
00834    if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &ztc)) {
00835       ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
00836       if (cnf->fd >= 0)
00837          close(cnf->fd);
00838       ao2_ref(cnf->usercontainer, -1);
00839       ast_mutex_destroy(&cnf->playlock);
00840       ast_mutex_destroy(&cnf->listenlock);
00841       ast_mutex_destroy(&cnf->recordthreadlock);
00842       ast_mutex_destroy(&cnf->announcethreadlock);
00843       free(cnf);
00844       cnf = NULL;
00845       goto cnfout;
00846    }
00847 
00848    cnf->zapconf = ztc.confno;
00849 
00850    /* Setup a new channel for playback of audio files */
00851    cnf->chan = ast_request(dahdi_chan_name, AST_FORMAT_SLINEAR, "pseudo", NULL);
00852    if (cnf->chan) {
00853       ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
00854       ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
00855       ztc.chan = 0;
00856       ztc.confno = cnf->zapconf;
00857       ztc.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
00858       if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &ztc)) {
00859          ast_log(LOG_WARNING, "Error setting conference\n");
00860          if (cnf->chan)
00861             ast_hangup(cnf->chan);
00862          else
00863             close(cnf->fd);
00864          ao2_ref(cnf->usercontainer, -1);
00865          ast_mutex_destroy(&cnf->playlock);
00866          ast_mutex_destroy(&cnf->listenlock);
00867          ast_mutex_destroy(&cnf->recordthreadlock);
00868          ast_mutex_destroy(&cnf->announcethreadlock);
00869          free(cnf);
00870          cnf = NULL;
00871          goto cnfout;
00872       }
00873    }
00874 
00875    /* Fill the conference struct */
00876    cnf->start = time(NULL);
00877    cnf->isdynamic = dynamic ? 1 : 0;
00878    if (option_verbose > 2)
00879       ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
00880    AST_LIST_INSERT_HEAD(&confs, cnf, list);
00881 
00882    /* Reserve conference number in map */
00883    if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
00884       conf_map[confno_int] = 1;
00885    
00886 cnfout:
00887    if (cnf)
00888       ast_atomic_fetchadd_int(&cnf->refcount, refcount);
00889 
00890    AST_LIST_UNLOCK(&confs);
00891 
00892    return cnf;
00893 }
00894 
00895 static int meetme_cmd(int fd, int argc, char **argv) 
00896 {
00897    /* Process the command */
00898    struct ast_conference *cnf;
00899    struct ast_conf_user *user;
00900    int hr, min, sec;
00901    int i = 0, total = 0;
00902    time_t now;
00903    char *header_format = "%-14s %-14s %-10s %-8s  %-8s\n";
00904    char *data_format = "%-12.12s   %4.4d        %4.4s       %02d:%02d:%02d  %-8s\n";
00905    char cmdline[1024] = "";
00906 
00907    if (argc > 8)
00908       ast_cli(fd, "Invalid Arguments.\n");
00909    /* Check for length so no buffer will overflow... */
00910    for (i = 0; i < argc; i++) {
00911       if (strlen(argv[i]) > 100)
00912          ast_cli(fd, "Invalid Arguments.\n");
00913    }
00914    if (argc == 1) {
00915       /* 'MeetMe': List all the conferences */  
00916       now = time(NULL);
00917       AST_LIST_LOCK(&confs);
00918       if (AST_LIST_EMPTY(&confs)) {
00919          ast_cli(fd, "No active MeetMe conferences.\n");
00920          AST_LIST_UNLOCK(&confs);
00921          return RESULT_SUCCESS;
00922       }
00923       ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
00924       AST_LIST_TRAVERSE(&confs, cnf, list) {
00925          if (cnf->markedusers == 0)
00926             strcpy(cmdline, "N/A ");
00927          else 
00928             snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
00929          hr = (now - cnf->start) / 3600;
00930          min = ((now - cnf->start) % 3600) / 60;
00931          sec = (now - cnf->start) % 60;
00932 
00933          ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
00934 
00935          total += cnf->users;    
00936       }
00937       AST_LIST_UNLOCK(&confs);
00938       ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
00939       return RESULT_SUCCESS;
00940    }
00941    if (argc < 3)
00942       return RESULT_SHOWUSAGE;
00943    ast_copy_string(cmdline, argv[2], sizeof(cmdline));   /* Argv 2: conference number */
00944    if (strstr(argv[1], "lock")) {   
00945       if (strcmp(argv[1], "lock") == 0) {
00946          /* Lock */
00947          strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
00948       } else {
00949          /* Unlock */
00950          strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
00951       }
00952    } else if (strstr(argv[1], "mute")) { 
00953       if (argc < 4)
00954          return RESULT_SHOWUSAGE;
00955       if (strcmp(argv[1], "mute") == 0) {
00956          /* Mute */
00957          if (strcmp(argv[3], "all") == 0) {
00958             strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
00959          } else {
00960             strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);   
00961             strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
00962          }
00963       } else {
00964          /* Unmute */
00965          if (strcmp(argv[3], "all") == 0) {
00966             strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
00967          } else {
00968             strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
00969             strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
00970          }
00971       }
00972    } else if (strcmp(argv[1], "kick") == 0) {
00973       if (argc < 4)
00974          return RESULT_SHOWUSAGE;
00975       if (strcmp(argv[3], "all") == 0) {
00976          /* Kick all */
00977          strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
00978       } else {
00979          /* Kick a single user */
00980          strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
00981          strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
00982       }  
00983    } else if(strcmp(argv[1], "list") == 0) {
00984       struct ao2_iterator user_iter;
00985       int concise = ( 4 == argc && ( !strcasecmp(argv[3], "concise") ) );
00986       /* List all the users in a conference */
00987       if (AST_LIST_EMPTY(&confs)) {
00988          if ( !concise )
00989             ast_cli(fd, "No active conferences.\n");
00990          return RESULT_SUCCESS;  
00991       }
00992       /* Find the right conference */
00993       AST_LIST_LOCK(&confs);
00994       AST_LIST_TRAVERSE(&confs, cnf, list) {
00995          if (strcmp(cnf->confno, argv[2]) == 0)
00996             break;
00997       }
00998       if (!cnf) {
00999          if ( !concise )
01000             ast_cli(fd, "No such conference: %s.\n",argv[2]);
01001          AST_LIST_UNLOCK(&confs);
01002          return RESULT_SUCCESS;
01003       }
01004       /* Show all the users */
01005       time(&now);
01006       user_iter = ao2_iterator_init(cnf->usercontainer, 0);
01007       while((user = ao2_iterator_next(&user_iter))) {
01008          hr = (now - user->jointime) / 3600;
01009          min = ((now - user->jointime) % 3600) / 60;
01010          sec = (now - user->jointime) % 60;
01011          if (!concise) {
01012             ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %02d:%02d:%02d\n",
01013                user->user_no,
01014                S_OR(user->chan->cid.cid_num, "<unknown>"),
01015                S_OR(user->chan->cid.cid_name, "<no name>"),
01016                user->chan->name,
01017                user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
01018                user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
01019                user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
01020                istalking(user->talking), hr, min, sec); 
01021          } else {
01022             ast_cli(fd, "%d!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
01023                user->user_no,
01024                S_OR(user->chan->cid.cid_num, ""),
01025                S_OR(user->chan->cid.cid_name, ""),
01026                user->chan->name,
01027                user->userflags  & CONFFLAG_ADMIN   ? "1" : "",
01028                user->userflags  & CONFFLAG_MONITOR ? "1" : "",
01029                user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)  ? "1" : "",
01030                user->talking, hr, min, sec);
01031          }
01032          ao2_ref(user, -1);
01033       }
01034       ao2_iterator_destroy(&user_iter);
01035       if ( !concise )
01036          ast_cli(fd,"%d users in that conference.\n",cnf->users);
01037       AST_LIST_UNLOCK(&confs);
01038       return RESULT_SUCCESS;
01039    } else 
01040       return RESULT_SHOWUSAGE;
01041    ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
01042    admin_exec(NULL, cmdline);
01043 
01044    return 0;
01045 }
01046 
01047 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
01048 {
01049    static char *cmds[] = {"lock", "unlock", "mute", "unmute", "kick", "list", NULL};
01050 
01051    int len = strlen(word);
01052    int which = 0;
01053    struct ast_conference *cnf = NULL;
01054    struct ast_conf_user *usr = NULL;
01055    char *confno = NULL;
01056    char usrno[50] = "";
01057    char *myline, *ret = NULL;
01058    
01059    if (pos == 1) {      /* Command */
01060       return ast_cli_complete(word, cmds, state);
01061    } else if (pos == 2) {  /* Conference Number */
01062       AST_LIST_LOCK(&confs);
01063       AST_LIST_TRAVERSE(&confs, cnf, list) {
01064          if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
01065             ret = cnf->confno;
01066             break;
01067          }
01068       }
01069       ret = ast_strdup(ret); /* dup before releasing the lock */
01070       AST_LIST_UNLOCK(&confs);
01071       return ret;
01072    } else if (pos == 3) {
01073       /* User Number || Conf Command option*/
01074       if (strstr(line, "mute") || strstr(line, "kick")) {
01075          if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
01076             return strdup("all");
01077          which++;
01078          AST_LIST_LOCK(&confs);
01079 
01080          /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
01081          myline = ast_strdupa(line);
01082          if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
01083             while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
01084                ;
01085          }
01086          
01087          AST_LIST_TRAVERSE(&confs, cnf, list) {
01088             if (!strcmp(confno, cnf->confno))
01089                 break;
01090          }
01091 
01092          if (cnf) {
01093             struct ao2_iterator user_iter;
01094             user_iter = ao2_iterator_init(cnf->usercontainer, 0);
01095             /* Search for the user */
01096             while((usr = ao2_iterator_next(&user_iter))) {
01097                snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
01098                if (!strncasecmp(word, usrno, len) && ++which > state) {
01099                   ao2_ref(usr, -1);
01100                   break;
01101                }
01102                ao2_ref(usr, -1);
01103             }
01104             ao2_iterator_destroy(&user_iter);
01105             AST_LIST_UNLOCK(&confs);
01106             return usr ? strdup(usrno) : NULL;
01107          }
01108          AST_LIST_UNLOCK(&confs);
01109       } else if ( strstr(line, "list") && ( 0 == state ) )
01110          return strdup("concise");
01111    }
01112 
01113    return NULL;
01114 }
01115    
01116 static char meetme_usage[] =
01117 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
01118 "       Executes a command for the conference or on a conferee\n";
01119 
01120 static const char *sla_hold_str(unsigned int hold_access)
01121 {
01122    const char *hold = "Unknown";
01123 
01124    switch (hold_access) {
01125    case SLA_HOLD_OPEN:
01126       hold = "Open";
01127       break;
01128    case SLA_HOLD_PRIVATE:
01129       hold = "Private";
01130    default:
01131       break;
01132    }
01133 
01134    return hold;
01135 }
01136 
01137 static int sla_show_trunks(int fd, int argc, char **argv)
01138 {
01139    const struct sla_trunk *trunk;
01140 
01141    ast_cli(fd, "\n"
01142                "=============================================================\n"
01143                "=== Configured SLA Trunks ===================================\n"
01144                "=============================================================\n"
01145                "===\n");
01146    AST_RWLIST_RDLOCK(&sla_trunks);
01147    AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
01148       struct sla_station_ref *station_ref;
01149       char ring_timeout[16] = "(none)";
01150       if (trunk->ring_timeout)
01151          snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
01152       ast_cli(fd, "=== ---------------------------------------------------------\n"
01153                   "=== Trunk Name:       %s\n"
01154                   "=== ==> Device:       %s\n"
01155                   "=== ==> AutoContext:  %s\n"
01156                   "=== ==> RingTimeout:  %s\n"
01157                   "=== ==> BargeAllowed: %s\n"
01158                   "=== ==> HoldAccess:   %s\n"
01159                   "=== ==> Stations ...\n",
01160                   trunk->name, trunk->device, 
01161                   S_OR(trunk->autocontext, "(none)"), 
01162                   ring_timeout,
01163                   trunk->barge_disabled ? "No" : "Yes",
01164                   sla_hold_str(trunk->hold_access));
01165       AST_RWLIST_RDLOCK(&sla_stations);
01166       AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
01167          ast_cli(fd, "===    ==> Station name: %s\n", station_ref->station->name);
01168       AST_RWLIST_UNLOCK(&sla_stations);
01169       ast_cli(fd, "=== ---------------------------------------------------------\n"
01170                   "===\n");
01171    }
01172    AST_RWLIST_UNLOCK(&sla_trunks);
01173    ast_cli(fd, "=============================================================\n"
01174                "\n");
01175 
01176    return RESULT_SUCCESS;
01177 }
01178 
01179 static const char *trunkstate2str(enum sla_trunk_state state)
01180 {
01181 #define S(e) case e: return # e;
01182    switch (state) {
01183    S(SLA_TRUNK_STATE_IDLE)
01184    S(SLA_TRUNK_STATE_RINGING)
01185    S(SLA_TRUNK_STATE_UP)
01186    S(SLA_TRUNK_STATE_ONHOLD)
01187    S(SLA_TRUNK_STATE_ONHOLD_BYME)
01188    }
01189    return "Uknown State";
01190 #undef S
01191 }
01192 
01193 static const char sla_show_trunks_usage[] =
01194 "Usage: sla show trunks\n"
01195 "       This will list all trunks defined in sla.conf\n";
01196 
01197 static int sla_show_stations(int fd, int argc, char **argv)
01198 {
01199    const struct sla_station *station;
01200 
01201    ast_cli(fd, "\n" 
01202                "=============================================================\n"
01203                "=== Configured SLA Stations =================================\n"
01204                "=============================================================\n"
01205                "===\n");
01206    AST_RWLIST_RDLOCK(&sla_stations);
01207    AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
01208       struct sla_trunk_ref *trunk_ref;
01209       char ring_timeout[16] = "(none)";
01210       char ring_delay[16] = "(none)";
01211       if (station->ring_timeout) {
01212          snprintf(ring_timeout, sizeof(ring_timeout), 
01213             "%u", station->ring_timeout);
01214       }
01215       if (station->ring_delay) {
01216          snprintf(ring_delay, sizeof(ring_delay), 
01217             "%u", station->ring_delay);
01218       }
01219       ast_cli(fd, "=== ---------------------------------------------------------\n"
01220                   "=== Station Name:    %s\n"
01221                   "=== ==> Device:      %s\n"
01222                   "=== ==> AutoContext: %s\n"
01223                   "=== ==> RingTimeout: %s\n"
01224                   "=== ==> RingDelay:   %s\n"
01225                   "=== ==> HoldAccess:  %s\n"
01226                   "=== ==> Trunks ...\n",
01227                   station->name, station->device,
01228                   S_OR(station->autocontext, "(none)"), 
01229                   ring_timeout, ring_delay,
01230                   sla_hold_str(station->hold_access));
01231       AST_RWLIST_RDLOCK(&sla_trunks);
01232       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
01233          if (trunk_ref->ring_timeout) {
01234             snprintf(ring_timeout, sizeof(ring_timeout),
01235                "%u", trunk_ref->ring_timeout);
01236          } else
01237             strcpy(ring_timeout, "(none)");
01238          if (trunk_ref->ring_delay) {
01239             snprintf(ring_delay, sizeof(ring_delay),
01240                "%u", trunk_ref->ring_delay);
01241          } else
01242             strcpy(ring_delay, "(none)");
01243          ast_cli(fd, "===    ==> Trunk Name: %s\n"
01244                      "===       ==> State:       %s\n"
01245                      "===       ==> RingTimeout: %s\n"
01246                      "===       ==> RingDelay:   %s\n",
01247                      trunk_ref->trunk->name,
01248                      trunkstate2str(trunk_ref->state),
01249                      ring_timeout, ring_delay);
01250       }
01251       AST_RWLIST_UNLOCK(&sla_trunks);
01252       ast_cli(fd, "=== ---------------------------------------------------------\n"
01253                   "===\n");
01254    }
01255    AST_RWLIST_UNLOCK(&sla_stations);
01256    ast_cli(fd, "============================================================\n"
01257                "\n");
01258 
01259    return RESULT_SUCCESS;
01260 }
01261 
01262 static const char sla_show_stations_usage[] =
01263 "Usage: sla show stations\n"
01264 "       This will list all stations defined in sla.conf\n";
01265 
01266 static struct ast_cli_entry cli_meetme[] = {
01267    { { "meetme", NULL, NULL },
01268    meetme_cmd, "Execute a command on a conference or conferee",
01269    meetme_usage, complete_meetmecmd },
01270 
01271    { { "sla", "show", "trunks", NULL },
01272    sla_show_trunks, "Show SLA Trunks",
01273    sla_show_trunks_usage, NULL },
01274 
01275    { { "sla", "show", "stations", NULL },
01276    sla_show_stations, "Show SLA Stations",
01277    sla_show_stations_usage, NULL },
01278 };
01279 
01280 static void conf_flush(int fd, struct ast_channel *chan)
01281 {
01282    int x;
01283 
01284    /* read any frames that may be waiting on the channel
01285       and throw them away
01286    */
01287    if (chan) {
01288       struct ast_frame *f;
01289 
01290       /* when no frames are available, this will wait
01291          for 1 millisecond maximum
01292       */
01293       while (ast_waitfor(chan, 1)) {
01294          f = ast_read(chan);
01295          if (f)
01296             ast_frfree(f);
01297          else /* channel was hung up or something else happened */
01298             break;
01299       }
01300    }
01301 
01302    /* flush any data sitting in the pseudo channel */
01303    x = DAHDI_FLUSH_ALL;
01304    if (ioctl(fd, DAHDI_FLUSH, &x))
01305       ast_log(LOG_WARNING, "Error flushing channel\n");
01306 
01307 }
01308 
01309 /* Remove the conference from the list and free it.
01310    We assume that this was called while holding conflock. */
01311 static int conf_free(struct ast_conference *conf)
01312 {
01313    int x;
01314    struct announce_listitem *item;
01315    
01316    AST_LIST_REMOVE(&confs, conf, list);
01317 
01318    if (conf->recording == MEETME_RECORD_ACTIVE) {
01319       conf->recording = MEETME_RECORD_TERMINATE;
01320       AST_LIST_UNLOCK(&confs);
01321       while (1) {
01322          usleep(1);
01323          AST_LIST_LOCK(&confs);
01324          if (conf->recording == MEETME_RECORD_OFF)
01325             break;
01326          AST_LIST_UNLOCK(&confs);
01327       }
01328    }
01329 
01330    for (x=0;x<AST_FRAME_BITS;x++) {
01331       if (conf->transframe[x])
01332          ast_frfree(conf->transframe[x]);
01333       if (conf->transpath[x])
01334          ast_translator_free_path(conf->transpath[x]);
01335    }
01336    if (conf->announcethread != AST_PTHREADT_NULL) {
01337       ast_mutex_lock(&conf->announcelistlock);
01338       conf->announcethread_stop = 1;
01339       ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
01340       ast_cond_signal(&conf->announcelist_addition);
01341       ast_mutex_unlock(&conf->announcelistlock);
01342       pthread_join(conf->announcethread, NULL);
01343    
01344       while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
01345          ast_filedelete(item->namerecloc, NULL);
01346          ao2_ref(item, -1);
01347       }
01348       ast_mutex_destroy(&conf->announcelistlock);
01349    }
01350    if (conf->origframe)
01351       ast_frfree(conf->origframe);
01352    if (conf->lchan)
01353       ast_hangup(conf->lchan);
01354    if (conf->chan)
01355       ast_hangup(conf->chan);
01356    if (conf->fd >= 0)
01357       close(conf->fd);
01358    if (conf->usercontainer) {
01359       ao2_ref(conf->usercontainer, -1);
01360    }
01361 
01362    ast_mutex_destroy(&conf->playlock);
01363    ast_mutex_destroy(&conf->listenlock);
01364    ast_mutex_destroy(&conf->recordthreadlock);
01365    ast_mutex_destroy(&conf->announcethreadlock);
01366 
01367    free(conf);
01368 
01369    return 0;
01370 }
01371 
01372 static void conf_queue_dtmf(const struct ast_conference *conf,
01373    const struct ast_conf_user *sender, struct ast_frame *f)
01374 {
01375    struct ast_conf_user *user;
01376    struct ao2_iterator user_iter;
01377 
01378    user_iter = ao2_iterator_init(conf->usercontainer, 0);
01379    while ((user = ao2_iterator_next(&user_iter))) {
01380       if (user == sender) {
01381          ao2_ref(user, -1);
01382          continue;
01383       }
01384       if (ast_write(user->chan, f) < 0)
01385          ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
01386       ao2_ref(user, -1);
01387    }
01388    ao2_iterator_destroy(&user_iter);
01389 }
01390 
01391 static void sla_queue_event_full(enum sla_event_type type, 
01392    struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
01393 {
01394    struct sla_event *event;
01395 
01396    if (sla.thread == AST_PTHREADT_NULL) {
01397       return;
01398    }
01399 
01400    if (!(event = ast_calloc(1, sizeof(*event))))
01401       return;
01402 
01403    event->type = type;
01404    event->trunk_ref = trunk_ref;
01405    event->station = station;
01406 
01407    if (!lock) {
01408       AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
01409       return;
01410    }
01411 
01412    ast_mutex_lock(&sla.lock);
01413    AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
01414    ast_cond_signal(&sla.cond);
01415    ast_mutex_unlock(&sla.lock);
01416 }
01417 
01418 static void sla_queue_event_nolock(enum sla_event_type type)
01419 {
01420    sla_queue_event_full(type, NULL, NULL, 0);
01421 }
01422 
01423 static void sla_queue_event(enum sla_event_type type)
01424 {
01425    sla_queue_event_full(type, NULL, NULL, 1);
01426 }
01427 
01428 /*! \brief Queue a SLA event from the conference */
01429 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
01430    struct ast_conference *conf)
01431 {
01432    struct sla_station *station;
01433    struct sla_trunk_ref *trunk_ref = NULL;
01434    char *trunk_name;
01435 
01436    trunk_name = ast_strdupa(conf->confno);
01437    strsep(&trunk_name, "_");
01438    if (ast_strlen_zero(trunk_name)) {
01439       ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
01440       return;
01441    }
01442 
01443    AST_RWLIST_RDLOCK(&sla_stations);
01444    AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
01445       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
01446          if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
01447             break;
01448       }
01449       if (trunk_ref)
01450          break;
01451    }
01452    AST_RWLIST_UNLOCK(&sla_stations);
01453 
01454    if (!trunk_ref) {
01455       ast_log(LOG_DEBUG, "Trunk not found for event!\n");
01456       return;
01457    }
01458 
01459    sla_queue_event_full(type, trunk_ref, station, 1);
01460 }
01461 
01462 /* Decrement reference counts, as incremented by find_conf() */
01463 static int dispose_conf(struct ast_conference *conf)
01464 {
01465    int res = 0;
01466    int confno_int = 0;
01467 
01468    AST_LIST_LOCK(&confs);
01469    if (ast_atomic_dec_and_test(&conf->refcount)) {
01470       /* Take the conference room number out of an inuse state */
01471       if ((sscanf(conf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
01472          conf_map[confno_int] = 0;
01473       conf_free(conf);
01474       res = 1;
01475    }
01476    AST_LIST_UNLOCK(&confs);
01477 
01478    return res;
01479 }
01480 
01481 static const char *get_announce_filename(enum announcetypes type)
01482 {
01483    switch (type) {
01484    case CONF_HASLEFT:
01485       return "conf-hasleft";
01486       break;
01487    case CONF_HASJOIN:
01488       return "conf-hasjoin";
01489       break;
01490    default:
01491       return "";
01492    }
01493 }
01494 
01495 static void *announce_thread(void *data)
01496 {
01497    struct announce_listitem *current;
01498    struct ast_conference *conf = data;
01499    int res;
01500    char filename[PATH_MAX] = "";
01501    AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
01502    AST_LIST_HEAD_INIT_NOLOCK(&local_list);
01503 
01504    while (!conf->announcethread_stop) {
01505       ast_mutex_lock(&conf->announcelistlock);
01506       if (conf->announcethread_stop) {
01507          ast_mutex_unlock(&conf->announcelistlock);
01508          break;
01509       }
01510       if (AST_LIST_EMPTY(&conf->announcelist))
01511          ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
01512 
01513       AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
01514       AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
01515 
01516       ast_mutex_unlock(&conf->announcelistlock);
01517       if (conf->announcethread_stop) {
01518          break;
01519       }
01520 
01521       for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
01522          ast_log(LOG_DEBUG, "About to play %s\n", current->namerecloc);
01523          if (!ast_fileexists(current->namerecloc, NULL, NULL))
01524             continue;
01525          if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
01526             if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
01527                res = ast_waitstream(current->confchan, "");
01528             if (!res) {
01529                ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
01530                if (!ast_streamfile(current->confchan, filename, current->language))
01531                   ast_waitstream(current->confchan, "");
01532             }
01533          }
01534          if (current->announcetype == CONF_HASLEFT) {
01535             ast_filedelete(current->namerecloc, NULL);
01536          }
01537       }
01538    }
01539 
01540    /* thread marked to stop, clean up */
01541    while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
01542       ast_filedelete(current->namerecloc, NULL);
01543       ao2_ref(current, -1);
01544    }
01545    return NULL;
01546 }
01547 
01548 static int can_write(struct ast_channel *chan, int confflags)
01549 {
01550    if (!(confflags & CONFFLAG_NO_AUDIO_UNTIL_UP)) {
01551       return 1;
01552    }
01553 
01554    return (chan->_state == AST_STATE_UP);
01555 }
01556 
01557 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
01558 {
01559    manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
01560          "Channel: %s\r\n"
01561          "Uniqueid: %s\r\n"
01562          "Meetme: %s\r\n"
01563          "Usernum: %d\r\n"
01564          "Status: %s\r\n",
01565          chan->name, chan->uniqueid, conf->confno, user->user_no, talking ? "on" : "off");
01566 }
01567 
01568 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
01569 {
01570    int last_talking = user->talking;
01571    if (last_talking == talking)
01572       return;
01573 
01574    user->talking = talking;
01575 
01576    if (monitor) {
01577       /* Check if talking state changed. Take care of -1 which means unmonitored */
01578       int was_talking = (last_talking > 0);
01579       int now_talking = (talking > 0);
01580       if (was_talking != now_talking) {
01581          send_talking_event(chan, conf, user, now_talking);
01582       }
01583    }
01584 }
01585 
01586 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
01587 {
01588    struct ast_conf_user *user = NULL;
01589    int fd;
01590    struct dahdi_confinfo ztc, ztc_empty;
01591    struct ast_frame *f;
01592    struct ast_channel *c;
01593    struct ast_frame fr;
01594    int outfd;
01595    int ms;
01596    int nfds;
01597    int res;
01598    int retryzap;
01599    int origfd;
01600    int musiconhold = 0, mohtempstopped = 0;
01601    int firstpass = 0;
01602    int lastmarked = 0;
01603    int currentmarked = 0;
01604    int ret = -1;
01605    int x;
01606    int menu_active = 0;
01607    int using_pseudo = 0;
01608    int duration=20;
01609    int hr, min, sec;
01610    int sent_event = 0;
01611    time_t now;
01612    struct ast_dsp *dsp=NULL;
01613    struct ast_app *app;
01614    const char *agifile;
01615    const char *agifiledefault = "conf-background.agi";
01616    char meetmesecs[30] = "";
01617    char exitcontext[AST_MAX_CONTEXT] = "";
01618    char recordingtmp[AST_MAX_EXTENSION] = "";
01619    char members[10] = "";
01620    int dtmf, opt_waitmarked_timeout = 0;
01621    time_t timeout = 0;
01622    struct dahdi_bufferinfo bi;
01623    char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
01624    char *buf = __buf + AST_FRIENDLY_OFFSET;
01625    int setusercount = 0;
01626    int confsilence = 0, totalsilence = 0;
01627 
01628    if (!(user = ao2_alloc(sizeof(*user), NULL))) {
01629       return ret;
01630    }
01631 
01632    /* Possible timeout waiting for marked user */
01633    if ((confflags & CONFFLAG_WAITMARKED) &&
01634       !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
01635       (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
01636       (opt_waitmarked_timeout > 0)) {
01637       timeout = time(NULL) + opt_waitmarked_timeout;
01638    }
01639 
01640    if (confflags & CONFFLAG_RECORDCONF) {
01641       if (!conf->recordingfilename) {
01642          conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
01643          if (!conf->recordingfilename) {
01644             snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
01645             conf->recordingfilename = ast_strdupa(recordingtmp);
01646          }
01647          conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
01648          if (!conf->recordingformat) {
01649             snprintf(recordingtmp, sizeof(recordingtmp), "wav");
01650             conf->recordingformat = ast_strdupa(recordingtmp);
01651          }
01652          ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
01653                 conf->confno, conf->recordingfilename, conf->recordingformat);
01654       }
01655    }
01656 
01657    ast_mutex_lock(&conf->recordthreadlock);
01658    if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request(dahdi_chan_name, AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
01659       ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
01660       ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
01661       ztc.chan = 0;
01662       ztc.confno = conf->zapconf;
01663       ztc.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
01664       if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &ztc)) {
01665          ast_log(LOG_WARNING, "Error starting listen channel\n");
01666          ast_hangup(conf->lchan);
01667          conf->lchan = NULL;
01668       } else {
01669          pthread_attr_init(&conf->attr);
01670          pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
01671          ast_pthread_create_background(&conf->recordthread, &conf->attr, recordthread, conf);
01672          pthread_attr_destroy(&conf->attr);
01673       }
01674    }
01675    ast_mutex_unlock(&conf->recordthreadlock);
01676 
01677    ast_mutex_lock(&conf->announcethreadlock);
01678    if ((conf->announcethread == AST_PTHREADT_NULL) && !(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
01679       ast_mutex_init(&conf->announcelistlock);
01680       AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
01681       ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
01682    }
01683    ast_mutex_unlock(&conf->announcethreadlock);
01684 
01685    time(&user->jointime);
01686 
01687    if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
01688       /* Sorry, but this confernce is locked! */   
01689       if (!ast_streamfile(chan, "conf-locked", chan->language))
01690          ast_waitstream(chan, "");
01691       goto outrun;
01692    }
01693 
01694       ast_mutex_lock(&conf->playlock);
01695    ao2_lock(conf->usercontainer);
01696    ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
01697    user->user_no++;
01698    ao2_link(conf->usercontainer, user);
01699    ao2_unlock(conf->usercontainer);
01700 
01701    user->chan = chan;
01702    user->userflags = confflags;
01703    user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
01704    user->talking = -1;
01705 
01706    ast_mutex_unlock(&conf->playlock);
01707 
01708    if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
01709       char destdir[PATH_MAX];
01710 
01711       snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
01712 
01713       if (mkdir(destdir, 0777) && errno != EEXIST) {
01714          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
01715          goto outrun;
01716       }
01717 
01718       snprintf(user->namerecloc, sizeof(user->namerecloc),
01719           "%s/meetme-username-%s-%d", destdir,
01720           conf->confno, user->user_no);
01721       if (confflags & CONFFLAG_INTROUSERNOREVIEW)
01722          res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
01723       else
01724          res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
01725       if (res == -1)
01726          goto outrun;
01727    }
01728 
01729    ast_mutex_lock(&conf->playlock);
01730 
01731    if (confflags & CONFFLAG_MARKEDUSER)
01732       conf->markedusers++;
01733    conf->users++;
01734    /* Update table */
01735    snprintf(members, sizeof(members), "%d", conf->users);
01736    ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL);
01737    setusercount = 1;
01738 
01739    /* This device changed state now - if this is the first user */
01740    if (conf->users == 1)
01741       ast_device_state_changed("meetme:%s", conf->confno);
01742 
01743    ast_mutex_unlock(&conf->playlock);
01744 
01745    if (confflags & CONFFLAG_EXIT_CONTEXT) {
01746       if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) 
01747          ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
01748       else if (!ast_strlen_zero(chan->macrocontext)) 
01749          ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
01750       else
01751          ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
01752    }
01753 
01754    /* Playback artbitrary intro message */
01755    if ((confflags & CONFFLAG_INTROMSG) &&
01756       !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
01757       if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], chan->language))
01758          ast_waitstream(chan, "");
01759    }
01760 
01761    if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
01762       if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
01763          if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
01764             ast_waitstream(chan, "");
01765       if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
01766          if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
01767             ast_waitstream(chan, "");
01768    }
01769 
01770    if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
01771       int keepplaying = 1;
01772 
01773       if (conf->users == 2) { 
01774          if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
01775             res = ast_waitstream(chan, AST_DIGIT_ANY);
01776             ast_stopstream(chan);
01777             if (res > 0)
01778                keepplaying=0;
01779             else if (res == -1)
01780                goto outrun;
01781          }
01782       } else { 
01783          if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
01784             res = ast_waitstream(chan, AST_DIGIT_ANY);
01785             ast_stopstream(chan);
01786             if (res > 0)
01787                keepplaying=0;
01788             else if (res == -1)
01789                goto outrun;
01790          }
01791          if (keepplaying) {
01792             res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
01793             if (res > 0)
01794                keepplaying=0;
01795             else if (res == -1)
01796                goto outrun;
01797          }
01798          if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
01799             res = ast_waitstream(chan, AST_DIGIT_ANY);
01800             ast_stopstream(chan);
01801             if (res > 0)
01802                keepplaying=0;
01803             else if (res == -1) 
01804                goto outrun;
01805          }
01806       }
01807    }
01808 
01809    if (!(confflags & CONFFLAG_NO_AUDIO_UNTIL_UP)) {
01810       /* We're leaving this alone until the state gets changed to up */
01811       ast_indicate(chan, -1);
01812    }
01813 
01814    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
01815       ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
01816       goto outrun;
01817    }
01818 
01819    if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
01820       ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
01821       goto outrun;
01822    }
01823 
01824    retryzap = (strcasecmp(chan->tech->type, dahdi_chan_name) || (chan->audiohooks || chan->monitor) ? 1 : 0);
01825    user->zapchannel = !retryzap;
01826 
01827  zapretry:
01828    origfd = chan->fds[0];
01829    if (retryzap) {
01830       /* open pseudo in non-blocking mode */
01831       fd = open(DAHDI_FILE_PSEUDO, O_RDWR | O_NONBLOCK);
01832       if (fd < 0) {
01833          ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
01834          goto outrun;
01835       }
01836       using_pseudo = 1;
01837       /* Setup buffering information */
01838       memset(&bi, 0, sizeof(bi));
01839       bi.bufsize = CONF_SIZE/2;
01840       bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
01841       bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
01842       bi.numbufs = audio_buffers;
01843       if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
01844          ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
01845          close(fd);
01846          goto outrun;
01847       }
01848       x = 1;
01849       if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
01850          ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
01851          close(fd);
01852          goto outrun;
01853       }
01854       nfds = 1;
01855    } else {
01856       /* XXX Make sure we're not running on a pseudo channel XXX */
01857       fd = chan->fds[0];
01858       nfds = 0;
01859    }
01860    memset(&ztc, 0, sizeof(ztc));
01861    memset(&ztc_empty, 0, sizeof(ztc_empty));
01862    /* Check to see if we're in a conference... */
01863    ztc.chan = 0;  
01864    if (ioctl(fd, DAHDI_GETCONF, &ztc)) {
01865       ast_log(LOG_WARNING, "Error getting conference\n");
01866       close(fd);
01867       goto outrun;
01868    }
01869    if (ztc.confmode) {
01870       /* Whoa, already in a conference...  Retry... */
01871       if (!retryzap) {
01872          ast_log(LOG_DEBUG, "%s channel is in a conference already, retrying with pseudo\n", dahdi_chan_name);
01873          retryzap = 1;
01874          goto zapretry;
01875       }
01876    }
01877    memset(&ztc, 0, sizeof(ztc));
01878    /* Add us to the conference */
01879    ztc.chan = 0;  
01880    ztc.confno = conf->zapconf;
01881 
01882    if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
01883       struct announce_listitem *item;
01884       if (!(item = ao2_alloc(sizeof(*item), NULL)))
01885          goto outrun;
01886       ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
01887       ast_copy_string(item->language, chan->language, sizeof(item->language));
01888       item->confchan = conf->chan;
01889       item->confusers = conf->users;
01890       item->announcetype = CONF_HASJOIN;
01891       ast_mutex_lock(&conf->announcelistlock);
01892       ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
01893       AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
01894       ast_cond_signal(&conf->announcelist_addition);
01895       ast_mutex_unlock(&conf->announcelistlock);
01896 
01897       while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
01898          ;
01899       }
01900       ao2_ref(item, -1);
01901    }
01902 
01903    if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers)
01904       ztc.confmode = DAHDI_CONF_CONF;
01905    else if (confflags & CONFFLAG_MONITOR)
01906       ztc.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
01907    else if (confflags & CONFFLAG_TALKER)
01908       ztc.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
01909    else 
01910       ztc.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
01911 
01912    if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
01913       ast_log(LOG_WARNING, "Error setting conference\n");
01914       close(fd);
01915       goto outrun;
01916    }
01917    if (option_debug) {
01918       ast_log(LOG_DEBUG, "Placed channel %s in %s conf %d\n", chan->name, dahdi_chan_name, conf->zapconf);
01919    }
01920 
01921    if (!sent_event) {
01922       manager_event(EVENT_FLAG_CALL, "MeetmeJoin", 
01923                "Channel: %s\r\n"
01924                "Uniqueid: %s\r\n"
01925                "Meetme: %s\r\n"
01926                "Usernum: %d\r\n",
01927                chan->name, chan->uniqueid, conf->confno, user->user_no);
01928       sent_event = 1;
01929    }
01930 
01931    if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
01932       firstpass = 1;
01933       if (!(confflags & CONFFLAG_QUIET))
01934          if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
01935             conf_play(chan, conf, ENTER);
01936    }
01937 
01938    conf_flush(fd, chan);
01939 
01940    if (dsp)
01941       ast_dsp_free(dsp);
01942 
01943    if (!(dsp = ast_dsp_new())) {
01944       ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
01945       res = -1;
01946    }
01947 
01948    if (confflags & CONFFLAG_AGI) {
01949       /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
01950          or use default filename of conf-background.agi */
01951 
01952       agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
01953       if (!agifile)
01954          agifile = agifiledefault;
01955 
01956       if (user->zapchannel) {
01957          /*  Set CONFMUTE mode on Zap channel to mute DTMF tones */
01958          x = 1;
01959          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
01960       }
01961       /* Find a pointer to the agi app and execute the script */
01962       app = pbx_findapp("agi");
01963       if (app) {
01964          char *s = ast_strdupa(agifile);
01965          ret = pbx_exec(chan, app, s);
01966       } else {
01967          ast_log(LOG_WARNING, "Could not find application (agi)\n");
01968          ret = -2;
01969       }
01970       if (user->zapchannel) {
01971          /*  Remove CONFMUTE mode on Zap channel */
01972          x = 0;
01973          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
01974       }
01975    } else {
01976       if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
01977          /*  Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
01978          x = 1;
01979          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
01980       }  
01981       for(;;) {
01982          int menu_was_active = 0;
01983 
01984          outfd = -1;
01985          ms = -1;
01986 
01987          if (timeout && time(NULL) >= timeout)
01988             break;
01989 
01990          /* if we have just exited from the menu, and the user had a channel-driver
01991             volume adjustment, restore it
01992          */
01993          if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
01994             set_talk_volume(user, user->listen.desired);
01995 
01996          menu_was_active = menu_active;
01997 
01998          currentmarked = conf->markedusers;
01999          if (!(confflags & CONFFLAG_QUIET) &&
02000              (confflags & CONFFLAG_MARKEDUSER) &&
02001              (confflags & CONFFLAG_WAITMARKED) &&
02002              lastmarked == 0) {
02003             if (currentmarked == 1 && conf->users > 1) {
02004                ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
02005                if (conf->users - 1 == 1) {
02006                   if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
02007                      ast_waitstream(chan, "");
02008                } else {
02009                   if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
02010                      ast_waitstream(chan, "");
02011                }
02012             }
02013             if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
02014                if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
02015                   ast_waitstream(chan, "");
02016          }
02017 
02018          /* Update the struct with the actual confflags */
02019          user->userflags = confflags;
02020 
02021          if (confflags & CONFFLAG_WAITMARKED) {
02022             if(currentmarked == 0) {
02023                if (lastmarked != 0) {
02024                   if (!(confflags & CONFFLAG_QUIET))
02025                      if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
02026                         ast_waitstream(chan, "");
02027                   if(confflags & CONFFLAG_MARKEDEXIT)
02028                      break;
02029                   else {
02030                      ztc.confmode = DAHDI_CONF_CONF;
02031                      if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
02032                         ast_log(LOG_WARNING, "Error setting conference\n");
02033                         close(fd);
02034                         goto outrun;
02035                      }
02036                   }
02037                }
02038                if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
02039                   ast_moh_start(chan, NULL, NULL);
02040                   musiconhold = 1;
02041                }
02042             } else if(currentmarked >= 1 && lastmarked == 0) {
02043                /* Marked user entered, so cancel timeout */
02044                timeout = 0;
02045                if (confflags & CONFFLAG_MONITOR)
02046                   ztc.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
02047                else if (confflags & CONFFLAG_TALKER)
02048                   ztc.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
02049                else
02050                   ztc.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
02051                if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
02052                   ast_log(LOG_WARNING, "Error setting conference\n");
02053                   close(fd);
02054                   goto outrun;
02055                }
02056                if (musiconhold && (confflags & CONFFLAG_MOH)) {
02057                   ast_moh_stop(chan);
02058                   musiconhold = 0;
02059                }
02060                if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
02061                   if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
02062                      ast_waitstream(chan, "");
02063                   conf_play(chan, conf, ENTER);
02064                }
02065             }
02066          }
02067 
02068          /* trying to add moh for single person conf */
02069          if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
02070             if (conf->users == 1) {
02071                if (musiconhold == 0) {
02072                   ast_moh_start(chan, NULL, NULL);
02073                   musiconhold = 1;
02074                } 
02075             } else {
02076                if (musiconhold) {
02077                   ast_moh_stop(chan);
02078                   musiconhold = 0;
02079                }
02080             }
02081          }
02082          
02083          /* Leave if the last marked user left */
02084          if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
02085             ret = -1;
02086             break;
02087          }
02088    
02089          /* Check if my modes have changed */
02090 
02091          /* If I should be muted but am still talker, mute me */
02092          if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & DAHDI_CONF_TALKER)) {
02093             ztc.confmode ^= DAHDI_CONF_TALKER;
02094             if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
02095                ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
02096                ret = -1;
02097                break;
02098             }
02099 
02100             /* Indicate user is not talking anymore - change him to unmonitored state */
02101             if ((confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER))) {
02102                set_user_talking(chan, conf, user, -1, confflags & CONFFLAG_MONITORTALKER);
02103             }
02104 
02105             manager_event(EVENT_FLAG_CALL, "MeetmeMute", 
02106                   "Channel: %s\r\n"
02107                   "Uniqueid: %s\r\n"
02108                   "Meetme: %s\r\n"
02109                   "Usernum: %i\r\n"
02110                   "Status: on\r\n",
02111                   chan->name, chan->uniqueid, conf->confno, user->user_no);
02112          }
02113 
02114          /* If I should be un-muted but am not talker, un-mute me */
02115          if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & DAHDI_CONF_TALKER)) {
02116             ztc.confmode |= DAHDI_CONF_TALKER;
02117             if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
02118                ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
02119                ret = -1;
02120                break;
02121             }
02122 
02123             manager_event(EVENT_FLAG_CALL, "MeetmeMute", 
02124                   "Channel: %s\r\n"
02125                   "Uniqueid: %s\r\n"
02126                   "Meetme: %s\r\n"
02127                   "Usernum: %i\r\n"
02128                   "Status: off\r\n",
02129                   chan->name, chan->uniqueid, conf->confno, user->user_no);
02130          }
02131 
02132          /* If I have been kicked, exit the conference */
02133          if (user->adminflags & ADMINFLAG_KICKME) {
02134             //You have been kicked.
02135             if (!(confflags & CONFFLAG_QUIET) && 
02136                !ast_streamfile(chan, "conf-kicked", chan->language)) {
02137                ast_waitstream(chan, "");
02138             }
02139             ret = 0;
02140             break;
02141          }
02142 
02143          c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
02144 
02145          if (c) {
02146             char dtmfstr[2] = "";
02147 
02148             if (c->fds[0] != origfd || (user->zapchannel && (c->audiohooks || c->monitor))) {
02149                if (using_pseudo) {
02150                   /* Kill old pseudo */
02151                   close(fd);
02152                   using_pseudo = 0;
02153                }
02154                ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
02155                retryzap = (strcasecmp(c->tech->type, dahdi_chan_name) || (c->audiohooks || c->monitor) ? 1 : 0);
02156                user->zapchannel = !retryzap;
02157                goto zapretry;
02158             }
02159             if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
02160                f = ast_read_noaudio(c);
02161             else
02162                f = ast_read(c);
02163             if (!f)
02164                break;
02165             if (f->frametype == AST_FRAME_DTMF) {
02166                dtmfstr[0] = f->subclass;
02167                dtmfstr[1] = '\0';
02168             }
02169 
02170             if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
02171                if (user->talk.actual)
02172                   ast_frame_adjust_volume(f, user->talk.actual);
02173 
02174                if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER)) {
02175                   if (user->talking == -1)
02176                      user->talking = 0;
02177 
02178                   res = ast_dsp_silence(dsp, f, &totalsilence);
02179                   if (totalsilence < MEETME_DELAYDETECTTALK) {
02180                      set_user_talking(chan, conf, user, 1, confflags & CONFFLAG_MONITORTALKER);
02181                   }
02182                   if (totalsilence > MEETME_DELAYDETECTENDTALK) {
02183                      set_user_talking(chan, conf, user, 0, confflags & CONFFLAG_MONITORTALKER);
02184                   }
02185                }
02186                if (using_pseudo) {
02187                   /* Absolutely do _not_ use careful_write here...
02188                      it is important that we read data from the channel
02189                      as fast as it arrives, and feed it into the conference.
02190                      The buffering in the pseudo channel will take care of any
02191                      timing differences, unless they are so drastic as to lose
02192                      audio frames (in which case carefully writing would only
02193                      have delayed the audio even further).
02194                   */
02195                   /* As it turns out, we do want to use careful write.  We just
02196                      don't want to block, but we do want to at least *try*
02197                      to write out all the samples.
02198                    */
02199                   if (user->talking || !(confflags & CONFFLAG_OPTIMIZETALKER))
02200                      careful_write(fd, f->data, f->datalen, 0);
02201                }
02202             } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
02203                if (confflags & CONFFLAG_PASS_DTMF)
02204                   conf_queue_dtmf(conf, user, f);
02205                ret = 0;
02206                ast_frfree(f);
02207                break;
02208             } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
02209                if (confflags & CONFFLAG_PASS_DTMF)
02210                   conf_queue_dtmf(conf, user, f);
02211                if (ioctl(fd, DAHDI_SETCONF, &ztc_empty)) {
02212                   ast_log(LOG_WARNING, "Error setting conference\n");
02213                   close(fd);
02214                   ast_frfree(f);
02215                   goto outrun;
02216                }
02217 
02218                /* if we are entering the menu, and the user has a channel-driver
02219                   volume adjustment, clear it
02220                */
02221                if (!menu_active && user->talk.desired && !user->talk.actual)
02222                   set_talk_volume(user, 0);
02223 
02224                if (musiconhold) {
02225                      ast_moh_stop(chan);
02226                }
02227                if ((confflags & CONFFLAG_ADMIN)) {
02228                   /* Admin menu */
02229                   if (!menu_active) {
02230                      menu_active = 1;
02231                      /* Record this sound! */
02232                      if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
02233                         dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
02234                         ast_stopstream(chan);
02235                      } else 
02236                         dtmf = 0;
02237                   } else 
02238                      dtmf = f->subclass;
02239                   if (dtmf) {
02240                      switch(dtmf) {
02241                      case '1': /* Un/Mute */
02242                         menu_active = 0;
02243 
02244                         /* for admin, change both admin and use flags */
02245                         if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
02246                            user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
02247                         else
02248                            user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
02249 
02250                         if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
02251                            if (!ast_streamfile(chan, "conf-muted", chan->language))
02252                               ast_waitstream(chan, "");
02253                         } else {
02254                            if (!ast_streamfile(chan, "conf-unmuted", chan->language))
02255                               ast_waitstream(chan, "");
02256                         }
02257                         break;
02258                      case '2': /* Un/Lock the Conference */
02259                         menu_active = 0;
02260                         if (conf->locked) {
02261                            conf->locked = 0;
02262                            if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
02263                               ast_waitstream(chan, "");
02264                         } else {
02265                            conf->locked = 1;
02266                            if (!ast_streamfile(chan, "conf-lockednow", chan->language))
02267                               ast_waitstream(chan, "");
02268                         }
02269                         break;
02270                      case '3': /* Eject last user */
02271                      {
02272                         struct ast_conf_user *usr = NULL;
02273                         int max_no = 0;
02274                         ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
02275                         menu_active = 0;
02276                         usr = ao2_find(conf->usercontainer, &max_no, 0);
02277                         if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
02278                            if(!ast_streamfile(chan, "conf-errormenu", chan->language))
02279                               ast_waitstream(chan, "");
02280                         } else {
02281                            usr->adminflags |= ADMINFLAG_KICKME;
02282                         }
02283                         ao2_ref(usr, -1);
02284                         ast_stopstream(chan);
02285                         break;   
02286                      }
02287                      case '4':
02288                         tweak_listen_volume(user, VOL_DOWN);
02289                         break;
02290                      case '6':
02291                         tweak_listen_volume(user, VOL_UP);
02292                         break;
02293                      case '7':
02294                         tweak_talk_volume(user, VOL_DOWN);
02295                         break;
02296                      case '8':
02297                         menu_active = 0;
02298                         break;
02299                      case '9':
02300                         tweak_talk_volume(user, VOL_UP);
02301                         break;
02302                      default:
02303                         menu_active = 0;
02304                         /* Play an error message! */
02305                         if (!ast_streamfile(chan, "conf-errormenu", chan->language))
02306                            ast_waitstream(chan, "");
02307                         break;
02308                      }
02309                   }
02310                } else {
02311                   /* User menu */
02312                   if (!menu_active) {
02313                      menu_active = 1;
02314                      if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
02315                         dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
02316                         ast_stopstream(chan);
02317                      } else
02318                         dtmf = 0;
02319                   } else 
02320                      dtmf = f->subclass;
02321                   if (dtmf) {
02322                      switch(dtmf) {
02323                      case '1': /* Un/Mute */
02324                         menu_active = 0;
02325 
02326                         /* user can only toggle the self-muted state */
02327                         user->adminflags ^= ADMINFLAG_SELFMUTED;
02328 
02329                         /* they can't override the admin mute state */
02330                         if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
02331                            if (!ast_streamfile(chan, "conf-muted", chan->language))
02332                               ast_waitstream(chan, "");
02333                         } else {
02334                            if (!ast_streamfile(chan, "conf-unmuted", chan->language))
02335                               ast_waitstream(chan, "");
02336                         }
02337                         break;
02338                      case '4':
02339                         tweak_listen_volume(user, VOL_DOWN);
02340                         break;
02341                      case '6':
02342                         tweak_listen_volume(user, VOL_UP);
02343                         break;
02344                      case '7':
02345                         tweak_talk_volume(user, VOL_DOWN);
02346                         break;
02347                      case '8':
02348                         menu_active = 0;
02349                         break;
02350                      case '9':
02351                         tweak_talk_volume(user, VOL_UP);
02352                         break;
02353                      default:
02354                         menu_active = 0;
02355                         if (!ast_streamfile(chan, "conf-errormenu", chan->language))
02356                            ast_waitstream(chan, "");
02357                         break;
02358                      }
02359                   }
02360                }
02361                if (musiconhold) {
02362                      ast_moh_start(chan, NULL, NULL);
02363                }
02364 
02365                if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
02366                   ast_log(LOG_WARNING, "Error setting conference\n");
02367                   close(fd);
02368                   ast_frfree(f);
02369                   goto outrun;
02370                }
02371 
02372                conf_flush(fd, chan);
02373             /* Since this option could absorb dtmf for the previous, we have to check this one last */
02374             } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
02375                if (confflags & CONFFLAG_PASS_DTMF)
02376                   conf_queue_dtmf(conf, user, f);
02377 
02378                if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
02379                   ast_log(LOG_DEBUG, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
02380                   ret = 0;
02381                   ast_frfree(f);
02382                   break;
02383                } else if (option_debug > 1)
02384                   ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension '%s' does not exist in context '%s'\n", dtmfstr, exitcontext);
02385             } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
02386                && confflags & CONFFLAG_PASS_DTMF) {
02387                conf_queue_dtmf(conf, user, f);
02388             } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
02389                switch (f->subclass) {
02390                case AST_CONTROL_HOLD:
02391                   sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
02392                   break;
02393                default:
02394                   break;
02395                }
02396             } else if (f->frametype == AST_FRAME_NULL) {
02397                /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
02398             } else if (f->frametype == AST_FRAME_CONTROL) {
02399                switch (f->subclass) {
02400                case AST_CONTROL_BUSY:
02401                case AST_CONTROL_CONGESTION:
02402                   ast_frfree(f);
02403                   goto outrun;
02404                   break;
02405                default:
02406                   if (option_debug) {
02407                      ast_log(LOG_DEBUG, 
02408                         "Got ignored control frame on channel %s, f->frametype=%d,f->subclass=%d\n",
02409                         chan->name, f->frametype, f->subclass);
02410                   }
02411                }
02412             } else if (option_debug) {
02413                ast_log(LOG_DEBUG,
02414                   "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
02415                   chan->name, f->frametype, f->subclass);
02416             }
02417             ast_frfree(f);
02418          } else if (outfd > -1) {
02419             res = read(outfd, buf, CONF_SIZE);
02420             if (res > 0) {
02421                memset(&fr, 0, sizeof(fr));
02422                fr.frametype = AST_FRAME_VOICE;
02423                fr.subclass = AST_FORMAT_SLINEAR;
02424                fr.datalen = res;
02425                fr.samples = res/2;
02426                fr.data = buf;
02427                fr.offset = AST_FRIENDLY_OFFSET;
02428                if (!user->listen.actual && 
02429                   ((confflags & CONFFLAG_MONITOR) || 
02430                    (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
02431                    (!user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
02432                    )) {
02433                   int index;
02434                   for (index=0;index<AST_FRAME_BITS;index++)
02435                      if (chan->rawwriteformat & (1 << index))
02436                         break;
02437                   if (index >= AST_FRAME_BITS)
02438                      goto bailoutandtrynormal;
02439                   ast_mutex_lock(&conf->listenlock);
02440                   if (!conf->transframe[index]) {
02441                      if (conf->origframe) {
02442                         if (musiconhold && !ast_dsp_silence(dsp, conf->origframe, &confsilence) && confsilence < MEETME_DELAYDETECTTALK) {
02443                            ast_moh_stop(chan);
02444                            mohtempstopped = 1;
02445                         }
02446                         if (!conf->transpath[index])
02447                            conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
02448                         if (conf->transpath[index]) {
02449                            conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
02450                            if (!conf->transframe[index])
02451                               conf->transframe[index] = &ast_null_frame;
02452                         }
02453                      }
02454                   }
02455                   if (conf->transframe[index]) {
02456                      if ((conf->transframe[index]->frametype != AST_FRAME_NULL) &&
02457                          can_write(chan, confflags)) {
02458                         struct ast_frame *cur;
02459                         /* the translator may have returned a list of frames, so
02460                            write each one onto the channel
02461                         */
02462                         for (cur = conf->transframe[index]; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
02463                            if (ast_write(chan, cur)) {
02464                               ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
02465                               break;
02466                            }
02467                         }
02468                         if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
02469                            mohtempstopped = 0;
02470                            ast_moh_start(chan, NULL, NULL);
02471                         }
02472                      }
02473                   } else {
02474                      ast_mutex_unlock(&conf->listenlock);
02475                      goto bailoutandtrynormal;
02476                   }
02477                   ast_mutex_unlock(&conf->listenlock);
02478                } else {
02479 bailoutandtrynormal:
02480                   if (musiconhold && !ast_dsp_silence(dsp, &fr, &confsilence) && confsilence < MEETME_DELAYDETECTTALK) {
02481                      ast_moh_stop(chan);
02482                      mohtempstopped = 1;
02483                   }
02484                   if (user->listen.actual)
02485                      ast_frame_adjust_volume(&fr, user->listen.actual);
02486                   if (can_write(chan, confflags) && ast_write(chan, &fr) < 0) {
02487                      ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
02488                   }
02489                   if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
02490                      mohtempstopped = 0;
02491                      ast_moh_start(chan, NULL, NULL);
02492                   }
02493                }
02494             } else 
02495                ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
02496          }
02497          lastmarked = currentmarked;
02498       }
02499    }
02500 
02501    if (musiconhold) {
02502       ast_moh_stop(chan);
02503    }
02504    
02505    if (using_pseudo)
02506       close(fd);
02507    else {
02508       /* Take out of conference */
02509       ztc.chan = 0;  
02510       ztc.confno = 0;
02511       ztc.confmode = 0;
02512       if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
02513          ast_log(LOG_WARNING, "Error setting conference\n");
02514       }
02515    }
02516 
02517    reset_volumes(user);
02518 
02519    if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
02520       conf_play(chan, conf, LEAVE);
02521 
02522    if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
02523       struct announce_listitem *item;
02524       if (!(item = ao2_alloc(sizeof(*item), NULL)))
02525          goto outrun;
02526       ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
02527       ast_copy_string(item->language, chan->language, sizeof(item->language));
02528       item->confchan = conf->chan;
02529       item->confusers = conf->users;
02530       item->announcetype = CONF_HASLEFT;
02531       ast_mutex_lock(&conf->announcelistlock);
02532       AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
02533       ast_cond_signal(&conf->announcelist_addition);
02534       ast_mutex_unlock(&conf->announcelistlock);
02535    } else if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users == 1) {
02536       /* Last person is leaving, so no reason to try and announce, but should delete the name recording */
02537       ast_filedelete(user->namerecloc, NULL);
02538    }
02539 
02540  outrun:
02541    AST_LIST_LOCK(&confs);
02542 
02543    if (dsp)
02544       ast_dsp_free(dsp);
02545    
02546    if (user->user_no) {
02547       /* Only cleanup users who really joined! */
02548       now = time(NULL);
02549       hr = (now - user->jointime) / 3600;
02550       min = ((now - user->jointime) % 3600) / 60;
02551       sec = (now - user->jointime) % 60;
02552 
02553       if (sent_event) {
02554          manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
02555                   "Channel: %s\r\n"
02556                   "Uniqueid: %s\r\n"
02557                   "Meetme: %s\r\n"
02558                   "Usernum: %d\r\n"
02559                   "CallerIDnum: %s\r\n"
02560                   "CallerIDname: %s\r\n"
02561                   "Duration: %ld\r\n",
02562                   chan->name, chan->uniqueid, conf->confno, 
02563                   user->user_no,
02564                   S_OR(user->chan->cid.cid_num, "<unknown>"),
02565                   S_OR(user->chan->cid.cid_name, "<unknown>"),
02566                   (long)(now - user->jointime));
02567       }
02568 
02569       if (setusercount) {
02570          conf->users--;
02571          /* Update table */
02572          snprintf(members, sizeof(members), "%d", conf->users);
02573          ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
02574          if (confflags & CONFFLAG_MARKEDUSER) 
02575             conf->markedusers--;
02576       }
02577       /* Remove ourselves from the container */
02578       ao2_unlink(conf->usercontainer, user); 
02579 
02580       /* Change any states */
02581       if (!conf->users)
02582          ast_device_state_changed("meetme:%s", conf->confno);
02583       
02584       /* Return the number of seconds the user was in the conf */
02585       snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
02586       pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
02587    }
02588    ao2_ref(user, -1);
02589    AST_LIST_UNLOCK(&confs);
02590 
02591    return ret;
02592 }
02593 
02594 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
02595                    char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
02596 {
02597    struct ast_variable *var, *save;
02598    struct ast_conference *cnf;
02599 
02600    /* Check first in the conference list */
02601    AST_LIST_LOCK(&confs);
02602    AST_LIST_TRAVERSE(&confs, cnf, list) {
02603       if (!strcmp(confno, cnf->confno)) 
02604          break;
02605    }
02606    if (cnf) {
02607       cnf->refcount += refcount;
02608    }
02609    AST_LIST_UNLOCK(&confs);
02610 
02611    if (!cnf) {
02612       char *pin = NULL, *pinadmin = NULL; /* For temp use */
02613       
02614       var = ast_load_realtime("meetme", "confno", confno, NULL);
02615 
02616       if (!var)
02617          return NULL;
02618 
02619       save = var;
02620       while (var) {
02621          if (!strcasecmp(var->name, "pin")) {
02622             pin = ast_strdupa(var->value);
02623          } else if (!strcasecmp(var->name, "adminpin")) {
02624             pinadmin = ast_strdupa(var->value);
02625          }
02626          var = var->next;
02627       }
02628       ast_variables_destroy(save);
02629       
02630       cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount);
02631    }
02632 
02633    if (cnf) {
02634       if (confflags && !cnf->chan &&
02635           !ast_test_flag(confflags, CONFFLAG_QUIET) &&
02636           ast_test_flag(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW)) {
02637          ast_log(LOG_WARNING, "No %s channel available for conference, user introduction disabled\n", dahdi_chan_name);
02638          ast_clear_flag(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW);
02639       }
02640       
02641       if (confflags && !cnf->chan &&
02642           ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
02643          ast_log(LOG_WARNING, "No %s channel available for conference, conference recording disabled\n", dahdi_chan_name);
02644          ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
02645       }
02646    }
02647 
02648    return cnf;
02649 }
02650 
02651 
02652 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
02653                char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
02654 {
02655    struct ast_config *cfg;
02656    struct ast_variable *var;
02657    struct ast_conference *cnf;
02658    AST_DECLARE_APP_ARGS(args,
02659       AST_APP_ARG(confno);
02660       AST_APP_ARG(pin);
02661       AST_APP_ARG(pinadmin);
02662    );
02663 
02664    /* Check first in the conference list */
02665    AST_LIST_LOCK(&confs);
02666    AST_LIST_TRAVERSE(&confs, cnf, list) {
02667       if (!strcmp(confno, cnf->confno)) 
02668          break;
02669    }
02670    if (cnf){
02671       cnf->refcount += refcount;
02672    }
02673    AST_LIST_UNLOCK(&confs);
02674 
02675    if (!cnf) {
02676       if (dynamic) {
02677          /* No need to parse meetme.conf */
02678          ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
02679          if (dynamic_pin) {
02680             if (dynamic_pin[0] == 'q') {
02681                /* Query the user to enter a PIN */
02682                if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
02683                   return NULL;
02684             }
02685             cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount);
02686          } else {
02687             cnf = build_conf(confno, "", "", make, dynamic, refcount);
02688          }
02689       } else {
02690          /* Check the config */
02691          cfg = ast_config_load(CONFIG_FILE_NAME);
02692          if (!cfg) {
02693             ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
02694             return NULL;
02695          }
02696 
02697          for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
02698             char parse[MAX_SETTINGS];
02699 
02700             if (strcasecmp(var->name, "conf"))
02701                continue;
02702 
02703             ast_copy_string(parse, var->value, sizeof(parse));
02704 
02705             AST_NONSTANDARD_APP_ARGS(args, parse, ',');
02706             if (!strcasecmp(args.confno, confno)) {
02707                /* Bingo it's a valid conference */
02708                cnf = build_conf(args.confno,
02709                      S_OR(args.pin, ""),
02710                      S_OR(args.pinadmin, ""),
02711                      make, dynamic, refcount);
02712                break;
02713             }
02714          }
02715          if (!var) {
02716             ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
02717          }
02718          ast_config_destroy(cfg);
02719       }
02720    } else if (dynamic_pin) {
02721       /* Correct for the user selecting 'D' instead of 'd' to have
02722          someone join into a conference that has already been created
02723          with a pin. */
02724       if (dynamic_pin[0] == 'q')
02725          dynamic_pin[0] = '\0';
02726    }
02727 
02728    if (cnf) {
02729       if (confflags && !cnf->chan &&
02730           !ast_test_flag(confflags, CONFFLAG_QUIET) &&
02731           ast_test_flag(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW)) {
02732          ast_log(LOG_WARNING, "No %s channel available for conference, user introduction disabled\n", dahdi_chan_name);
02733          ast_clear_flag(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW);
02734       }
02735       
02736       if (confflags && !cnf->chan &&
02737           ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
02738          ast_log(LOG_WARNING, "No %s channel available for conference, conference recording disabled\n", dahdi_chan_name);
02739          ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
02740       }
02741    }
02742 
02743    return cnf;
02744 }
02745 
02746 /*! \brief The MeetmeCount application */
02747 static int count_exec(struct ast_channel *chan, void *data)
02748 {
02749    struct ast_module_user *u;
02750    int res = 0;
02751    struct ast_conference *conf;
02752    int count;
02753    char *localdata;
02754    char val[80] = "0"; 
02755    AST_DECLARE_APP_ARGS(args,
02756       AST_APP_ARG(confno);
02757       AST_APP_ARG(varname);
02758    );
02759 
02760    if (ast_strlen_zero(data)) {
02761       ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
02762       return -1;
02763    }
02764 
02765    u = ast_module_user_add(chan);
02766    
02767    if (!(localdata = ast_strdupa(data))) {
02768       ast_module_user_remove(u);
02769       return -1;
02770    }
02771 
02772    AST_STANDARD_APP_ARGS(args, localdata);
02773    
02774    conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
02775 
02776    if (conf) {
02777       count = conf->users;
02778       dispose_conf(conf);
02779       conf = NULL;
02780    } else
02781       count = 0;
02782 
02783    if (!ast_strlen_zero(args.varname)){
02784       /* have var so load it and exit */
02785       snprintf(val, sizeof(val), "%d",count);
02786       pbx_builtin_setvar_helper(chan, args.varname, val);
02787    } else {
02788       if (chan->_state != AST_STATE_UP)
02789          ast_answer(chan);
02790       res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
02791    }
02792    ast_module_user_remove(u);
02793 
02794    return res;
02795 }
02796 
02797 /*! \brief The meetme() application */
02798 static int conf_exec(struct ast_channel *chan, void *data)
02799 {
02800    int res=-1;
02801    struct ast_module_user *u;
02802    char confno[MAX_CONFNUM] = "";
02803    int allowretry = 0;
02804    int retrycnt = 0;
02805    struct ast_conference *cnf = NULL;
02806    struct ast_flags confflags = {0};
02807    int dynamic = 0;
02808    int empty = 0, empty_no_pin = 0;
02809    int always_prompt = 0;
02810    char *notdata, *info, the_pin[MAX_PIN] = "";
02811    AST_DECLARE_APP_ARGS(args,
02812       AST_APP_ARG(confno);
02813       AST_APP_ARG(options);
02814       AST_APP_ARG(pin);
02815    );
02816    char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
02817 
02818    u = ast_module_user_add(chan);
02819 
02820    if (ast_strlen_zero(data)) {
02821       allowretry = 1;
02822       notdata = "";
02823    } else {
02824       notdata = data;
02825    }
02826    
02827    if (chan->_state != AST_STATE_UP)
02828       ast_answer(chan);
02829 
02830    info = ast_strdupa(notdata);
02831 
02832    AST_STANDARD_APP_ARGS(args, info);  
02833 
02834    if (args.confno) {
02835       ast_copy_string(confno, args.confno, sizeof(confno));
02836       if (ast_strlen_zero(confno)) {
02837          allowretry = 1;
02838       }
02839    }
02840    
02841    if (args.pin)
02842       ast_copy_string(the_pin, args.pin, sizeof(the_pin));
02843 
02844    if (args.options) {
02845       ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
02846       dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
02847       if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && ast_strlen_zero(args.pin))
02848          strcpy(the_pin, "q");
02849 
02850       empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
02851       empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
02852       always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT | CONFFLAG_DYNAMICPIN);
02853    }
02854 
02855    do {
02856       if (retrycnt > 3)
02857          allowretry = 0;
02858       if (empty) {
02859          int i;
02860          struct ast_config *cfg;
02861          struct ast_variable *var;
02862          int confno_int;
02863 
02864          /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
02865          if ((empty_no_pin) || (!dynamic)) {
02866             cfg = ast_config_load(CONFIG_FILE_NAME);
02867             if (cfg) {
02868                var = ast_variable_browse(cfg, "rooms");
02869                while (var) {
02870                   char parse[MAX_SETTINGS], *stringp = parse, *confno_tmp;
02871                   if (!strcasecmp(var->name, "conf")) {
02872                      int found = 0;
02873                      ast_copy_string(parse, var->value, sizeof(parse));
02874                      confno_tmp = strsep(&stringp, "|,");
02875                      if (!dynamic) {
02876                         /* For static:  run through the list and see if this conference is empty */
02877                         AST_LIST_LOCK(&confs);
02878                         AST_LIST_TRAVERSE(&confs, cnf, list) {
02879                            if (!strcmp(confno_tmp, cnf->confno)) {
02880                               /* The conference exists, therefore it's not empty */
02881                               found = 1;
02882                               break;
02883                            }
02884                         }
02885                         AST_LIST_UNLOCK(&confs);
02886                         if (!found) {
02887                            /* At this point, we have a confno_tmp (static conference) that is empty */
02888                            if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
02889                               /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
02890                                * Case 2:  empty_no_pin and pin is blank (but not NULL)
02891                                * Case 3:  not empty_no_pin
02892                                */
02893                               ast_copy_string(confno, confno_tmp, sizeof(confno));
02894                               break;
02895                               /* XXX the map is not complete (but we do have a confno) */
02896                            }
02897                         }
02898                      }
02899                   }
02900                   var = var->next;
02901                }
02902                ast_config_destroy(cfg);
02903             }
02904          }
02905 
02906          /* Select first conference number not in use */
02907          if (ast_strlen_zero(confno) && dynamic) {
02908             AST_LIST_LOCK(&confs);
02909             for (i = 0; i < sizeof(conf_map) / sizeof(conf_map[0]); i++) {
02910                if (!conf_map[i]) {
02911                   snprintf(confno, sizeof(confno), "%d", i);
02912                   conf_map[i] = 1;
02913                   break;
02914                }
02915             }
02916             AST_LIST_UNLOCK(&confs);
02917          }
02918 
02919          /* Not found? */
02920          if (ast_strlen_zero(confno)) {
02921             res = ast_streamfile(chan, "conf-noempty", chan->language);
02922             if (!res)
02923                ast_waitstream(chan, "");
02924          } else {
02925             if (sscanf(confno, "%30d", &confno_int) == 1) {
02926                res = ast_streamfile(chan, "conf-enteringno", chan->language);
02927                if (!res) {
02928                   ast_waitstream(chan, "");
02929                   res = ast_say_digits(chan, confno_int, "", chan->language);
02930                }
02931             } else {
02932                ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
02933             }
02934          }
02935       }
02936 
02937       while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
02938          /* Prompt user for conference number */
02939          res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
02940          if (res < 0) {
02941             /* Don't try to validate when we catch an error */
02942             confno[0] = '\0';
02943             allowretry = 0;
02944             break;
02945          }
02946       }
02947       if (!ast_strlen_zero(confno)) {
02948          /* Check the validity of the conference */
02949          cnf = find_conf(chan, confno, 1, dynamic, the_pin, 
02950             sizeof(the_pin), 1, &confflags);
02951          if (!cnf) {
02952             cnf = find_conf_realtime(chan, confno, 1, dynamic, 
02953                the_pin, sizeof(the_pin), 1, &confflags);
02954          }
02955 
02956          if (!cnf) {
02957             res = ast_streamfile(chan, "conf-invalid", chan->language);
02958             if (!res)
02959                ast_waitstream(chan, "");
02960             res = -1;
02961             if (allowretry)
02962                confno[0] = '\0';
02963          } else {
02964             if (((!ast_strlen_zero(cnf->pin)       &&
02965                !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
02966                  (!ast_strlen_zero(cnf->pinadmin)  &&
02967                    ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
02968                     (!ast_strlen_zero(cnf->pin) &&
02969                       ast_strlen_zero(cnf->pinadmin) &&
02970                       ast_test_flag(&confflags, CONFFLAG_ADMIN))) &&
02971                 (!(cnf->users == 0 && cnf->isdynamic))) {
02972                char pin[MAX_PIN] = "";
02973                int j;
02974 
02975                /* Allow the pin to be retried up to 3 times */
02976                for (j = 0; j < 3; j++) {
02977                   if (*the_pin && (always_prompt == 0)) {
02978                      ast_copy_string(pin, the_pin, sizeof(pin));
02979                      res = 0;
02980                   } else {
02981                      /* Prompt user for pin if pin is required */
02982                      res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
02983                   }
02984                   if (res >= 0) {
02985                      if ((!strcasecmp(pin, cnf->pin) &&
02986                           (ast_strlen_zero(cnf->pinadmin) ||
02987                            !ast_test_flag(&confflags, CONFFLAG_ADMIN))) ||
02988                           (!ast_strlen_zero(cnf->pinadmin) &&
02989                            !strcasecmp(pin, cnf->pinadmin))) {
02990                         /* Pin correct */
02991                         allowretry = 0;
02992                         if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) 
02993                            ast_set_flag(&confflags, CONFFLAG_ADMIN);
02994                         /* Run the conference */
02995                         res = conf_run(chan, cnf, confflags.flags, optargs);
02996                         break;
02997                      } else {
02998                         /* Pin invalid */
02999                         if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
03000                            res = ast_waitstream(chan, AST_DIGIT_ANY);
03001                            ast_stopstream(chan);
03002                         }
03003                         else {
03004                            ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
03005                            break;
03006                         }
03007                         if (res < 0)
03008                            break;
03009                         pin[0] = res;
03010                         pin[1] = '\0';
03011                         res = -1;
03012                         if (allowretry)
03013                            confno[0] = '\0';
03014                      }
03015                   } else {
03016                      /* failed when getting the pin */
03017                      res = -1;
03018                      allowretry = 0;
03019                      /* see if we need to get rid of the conference */
03020                      break;
03021                   }
03022 
03023                   /* Don't retry pin with a static pin */
03024                   if (*the_pin && (always_prompt==0)) {
03025                      break;
03026                   }
03027                }
03028             } else {
03029                /* No pin required */
03030                allowretry = 0;
03031 
03032                /* Run the conference */
03033                res = conf_run(chan, cnf, confflags.flags, optargs);
03034             }
03035             dispose_conf(cnf);
03036             cnf = NULL;
03037          }
03038       }
03039    } while (allowretry);
03040 
03041    if (cnf)
03042       dispose_conf(cnf);
03043 
03044    ast_module_user_remove(u);
03045    
03046    return res;
03047 }
03048 
03049 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident) 
03050 {
03051    struct ast_conf_user *user = NULL;
03052    int cid;
03053    
03054    sscanf(callerident, "%30i", &cid);
03055    if (conf && callerident) {
03056       user = ao2_find(conf->usercontainer, &cid, 0);
03057       /* reference decremented later in admin_exec */
03058       return user;
03059    }
03060    return NULL;
03061 }
03062 
03063 static int user_set_kickme_cb(void *obj, void *unused, int flags)
03064 {
03065    struct ast_conf_user *user = obj;
03066    user->adminflags |= ADMINFLAG_KICKME;
03067    return 0;
03068 }
03069 
03070 static int user_set_muted_cb(void *obj, void *unused, int flags)
03071 {
03072    struct ast_conf_user *user = obj;
03073    if (!(user->userflags & CONFFLAG_ADMIN)) {
03074       user->adminflags |= ADMINFLAG_MUTED;
03075    }
03076    return 0;
03077 }
03078 
03079 static int user_set_unmuted_cb(void *obj, void *unused, int flags)
03080 {
03081    struct ast_conf_user *user = obj;
03082    user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
03083    return 0;
03084 }
03085 
03086 static int user_listen_volup_cb(void *obj, void *unused, int flags)
03087 {
03088    struct ast_conf_user *user = obj;
03089    tweak_listen_volume(user, VOL_UP);
03090    return 0;
03091 }
03092 
03093 static int user_listen_voldown_cb(void *obj, void *unused, int flags)
03094 {
03095    struct ast_conf_user *user = obj;
03096    tweak_listen_volume(user, VOL_DOWN);
03097    return 0;
03098 }
03099 
03100 static int user_talk_volup_cb(void *obj, void *unused, int flags)
03101 {
03102    struct ast_conf_user *user = obj;
03103    tweak_talk_volume(user, VOL_UP);
03104    return 0;
03105 }
03106 
03107 static int user_talk_voldown_cb(void *obj, void *unused, int flags)
03108 {
03109    struct ast_conf_user *user = obj;
03110    tweak_talk_volume(user, VOL_DOWN);
03111    return 0;
03112 }
03113 
03114 static int user_reset_vol_cb(void *obj, void *unused, int flags)
03115 {
03116    struct ast_conf_user *user = obj;
03117    reset_volumes(user);
03118    return 0;
03119 }
03120 
03121 /*! \brief The MeetMeadmin application */
03122 /* MeetMeAdmin(confno, command, caller) */
03123 static int admin_exec(struct ast_channel *chan, void *data) {
03124    char *params;
03125    struct ast_conference *cnf;
03126    struct ast_conf_user *user = NULL;
03127    struct ast_module_user *u;
03128    AST_DECLARE_APP_ARGS(args,
03129       AST_APP_ARG(confno);
03130       AST_APP_ARG(command);
03131       AST_APP_ARG(user);
03132    );
03133 
03134    if (ast_strlen_zero(data)) {
03135       ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
03136       return -1;
03137    }
03138 
03139    u = ast_module_user_add(chan);
03140 
03141    AST_LIST_LOCK(&confs);
03142    
03143    params = ast_strdupa(data);
03144    AST_STANDARD_APP_ARGS(args, params);
03145 
03146    if (!args.command) {
03147       ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
03148       AST_LIST_UNLOCK(&confs);
03149       ast_module_user_remove(u);
03150       return -1;
03151    }
03152    AST_LIST_TRAVERSE(&confs, cnf, list) {
03153       if (!strcmp(cnf->confno, args.confno))
03154          break;
03155    }
03156 
03157    if (!cnf) {
03158       ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
03159       AST_LIST_UNLOCK(&confs);
03160       ast_module_user_remove(u);
03161       return 0;
03162    }
03163 
03164    ast_atomic_fetchadd_int(&cnf->refcount, 1);
03165 
03166    if (args.user) {
03167       user = find_user(cnf, args.user);
03168       if (!user) {
03169          ast_log(LOG_NOTICE, "Specified User not found!\n");
03170          goto usernotfound;
03171       }
03172    }
03173 
03174    switch (*args.command) {
03175    case 76: /* L: Lock */ 
03176       cnf->locked = 1;
03177       break;
03178    case 108: /* l: Unlock */ 
03179       cnf->locked = 0;
03180       break;
03181    case 75: /* K: kick all users */
03182       ao2_callback(cnf->usercontainer, 0, user_set_kickme_cb, NULL);
03183       break;
03184    case 101: /* e: Eject last user*/
03185    {
03186       int max_no = 0;
03187       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
03188       user = ao2_find(cnf->usercontainer, &max_no, 0);
03189       if (!(user->userflags & CONFFLAG_ADMIN))
03190          user->adminflags |= ADMINFLAG_KICKME;
03191       else
03192          ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
03193       ao2_ref(user, -1);
03194       break;
03195    }
03196    case 77: /* M: Mute */ 
03197       user->adminflags |= ADMINFLAG_MUTED;
03198       break;
03199    case 78: /* N: Mute all (non-admin) users */
03200       ao2_callback(cnf->usercontainer, 0, user_set_muted_cb, NULL);
03201       break;               
03202    case 109: /* m: Unmute */ 
03203       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
03204       break;
03205    case 110: /* n: Unmute all users */
03206       ao2_callback(cnf->usercontainer, 0, user_set_unmuted_cb, NULL);
03207       break;
03208    case 107: /* k: Kick user */ 
03209       user->adminflags |= ADMINFLAG_KICKME;
03210       break;
03211    case 118: /* v: Lower all users listen volume */
03212       ao2_callback(cnf->usercontainer, 0, user_listen_voldown_cb, NULL);
03213       break;
03214    case 86: /* V: Raise all users listen volume */
03215       ao2_callback(cnf->usercontainer, 0, user_listen_volup_cb, NULL);
03216       break;
03217    case 115: /* s: Lower all users speaking volume */
03218       ao2_callback(cnf->usercontainer, 0, user_talk_voldown_cb, NULL);
03219       break;
03220    case 83: /* S: Raise all users speaking volume */
03221       ao2_callback(cnf->usercontainer, 0, user_talk_volup_cb, NULL);
03222       break;
03223    case 82: /* R: Reset all volume levels */
03224       ao2_callback(cnf->usercontainer, 0, user_reset_vol_cb, NULL);
03225       break;
03226    case 114: /* r: Reset user's volume level */
03227       reset_volumes(user);
03228       break;
03229    case 85: /* U: Raise user's listen volume */
03230       tweak_listen_volume(user, VOL_UP);
03231       break;
03232    case 117: /* u: Lower user's listen volume */
03233       tweak_listen_volume(user, VOL_DOWN);
03234       break;
03235    case 84: /* T: Raise user's talk volume */
03236       tweak_talk_volume(user, VOL_UP);
03237       break;
03238    case 116: /* t: Lower user's talk volume */
03239       tweak_talk_volume(user, VOL_DOWN);
03240       break;
03241    }
03242 
03243    if (args.user) {
03244       /* decrement reference from find_user */
03245       ao2_ref(user, -1);
03246    }
03247 usernotfound:
03248    AST_LIST_UNLOCK(&confs);
03249 
03250    dispose_conf(cnf);
03251 
03252    ast_module_user_remove(u);
03253    
03254    return 0;
03255 }
03256 
03257 static int meetmemute(struct mansession *s, const struct message *m, int mute)
03258 {
03259    struct ast_conference *conf;
03260    struct ast_conf_user *user;
03261    const char *confid = astman_get_header(m, "Meetme");
03262    char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
03263    int userno;
03264 
03265    if (ast_strlen_zero(confid)) {
03266       astman_send_error(s, m, "Meetme conference not specified");
03267       return 0;
03268    }
03269 
03270    if (ast_strlen_zero(userid)) {
03271       astman_send_error(s, m, "Meetme user number not specified");
03272       return 0;
03273    }
03274 
03275    userno = strtoul(userid, &userid, 10);
03276 
03277    if (*userid) {
03278       astman_send_error(s, m, "Invalid user number");
03279       return 0;
03280    }
03281 
03282    /* Look in the conference list */
03283    AST_LIST_LOCK(&confs);
03284    AST_LIST_TRAVERSE(&confs, conf, list) {
03285       if (!strcmp(confid, conf->confno))
03286          break;
03287    }
03288 
03289    if (!conf) {
03290       AST_LIST_UNLOCK(&confs);
03291       astman_send_error(s, m, "Meetme conference does not exist");
03292       return 0;
03293    }
03294 
03295    user = ao2_find(conf->usercontainer, &userno, 0);
03296 
03297    if (!user) {
03298       AST_LIST_UNLOCK(&confs);
03299       astman_send_error(s, m, "User number not found");
03300       return 0;
03301    }
03302 
03303    if (mute)
03304       user->adminflags |= ADMINFLAG_MUTED;   /* request user muting */
03305    else
03306       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);  /* request user unmuting */
03307 
03308    AST_LIST_UNLOCK(&confs);
03309 
03310    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);
03311 
03312    ao2_ref(user, -1);
03313    astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
03314    return 0;
03315 }
03316 
03317 static int action_meetmemute(struct mansession *s, const struct message *m)
03318 {
03319    return meetmemute(s, m, 1);
03320 }
03321 
03322 static int action_meetmeunmute(struct mansession *s, const struct message *m)
03323 {
03324    return meetmemute(s, m, 0);
03325 }
03326 
03327 static void *recordthread(void *args)
03328 {
03329    struct ast_conference *cnf = args;
03330    struct ast_frame *f=NULL;
03331    int flags;
03332    struct ast_filestream *s=NULL;
03333    int res=0;
03334    int x;
03335    const char *oldrecordingfilename = NULL;
03336 
03337    if (!cnf || !cnf->lchan) {
03338       pthread_exit(0);
03339    }
03340 
03341    ast_stopstream(cnf->lchan);
03342    flags = O_CREAT|O_TRUNC|O_WRONLY;
03343 
03344 
03345    cnf->recording = MEETME_RECORD_ACTIVE;
03346    while (ast_waitfor(cnf->lchan, -1) > -1) {
03347       if (cnf->recording == MEETME_RECORD_TERMINATE) {
03348          AST_LIST_LOCK(&confs);
03349          AST_LIST_UNLOCK(&confs);
03350          break;
03351       }
03352       if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
03353          s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
03354          oldrecordingfilename = cnf->recordingfilename;
03355       }
03356       
03357       f = ast_read(cnf->lchan);
03358       if (!f) {
03359          res = -1;
03360          break;
03361       }
03362       if (f->frametype == AST_FRAME_VOICE) {
03363          ast_mutex_lock(&cnf->listenlock);
03364          for (x=0;x<AST_FRAME_BITS;x++) {
03365             /* Free any translations that have occured */
03366             if (cnf->transframe[x]) {
03367                ast_frfree(cnf->transframe[x]);
03368                cnf->transframe[x] = NULL;
03369             }
03370          }
03371          if (cnf->origframe)
03372             ast_frfree(cnf->origframe);
03373          cnf->origframe = ast_frdup(f);
03374          ast_mutex_unlock(&cnf->listenlock);
03375          if (s)
03376             res = ast_writestream(s, f);
03377          if (res) {
03378             ast_frfree(f);
03379             break;
03380          }
03381       }
03382       ast_frfree(f);
03383    }
03384    cnf->recording = MEETME_RECORD_OFF;
03385    if (s)
03386       ast_closestream(s);
03387    
03388    pthread_exit(0);
03389 }
03390 
03391 /*! \brief Callback for devicestate providers */
03392 static int meetmestate(const char *data)
03393 {
03394    struct ast_conference *conf;
03395 
03396    /* Find conference */
03397    AST_LIST_LOCK(&confs);
03398    AST_LIST_TRAVERSE(&confs, conf, list) {
03399       if (!strcmp(data, conf->confno))
03400          break;
03401    }
03402    AST_LIST_UNLOCK(&confs);
03403    if (!conf)
03404       return AST_DEVICE_INVALID;
03405 
03406 
03407    /* SKREP to fill */
03408    if (!conf->users)
03409       return AST_DEVICE_NOT_INUSE;
03410 
03411    return AST_DEVICE_INUSE;
03412 }
03413 
03414 static void load_config_meetme(void)
03415 {
03416    struct ast_config *cfg;
03417    const char *val;
03418 
03419    audio_buffers = DEFAULT_AUDIO_BUFFERS;
03420 
03421    if (!(cfg = ast_config_load(CONFIG_FILE_NAME)))
03422       return;
03423 
03424    if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
03425       if ((sscanf(val, "%30d", &audio_buffers) != 1)) {
03426          ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
03427          audio_buffers = DEFAULT_AUDIO_BUFFERS;
03428       } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
03429          ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
03430             DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
03431          audio_buffers = DEFAULT_AUDIO_BUFFERS;
03432       }
03433       if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
03434          ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
03435    }
03436 
03437    ast_config_destroy(cfg);
03438 }
03439 
03440 /*! \brief Find an SLA trunk by name
03441  * \note This must be called with the sla_trunks container locked
03442  */
03443 static struct sla_trunk *sla_find_trunk(const char *name)
03444 {
03445    struct sla_trunk *trunk = NULL;
03446 
03447    AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
03448       if (!strcasecmp(trunk->name, name))
03449          break;
03450    }
03451 
03452    return trunk;
03453 }
03454 
03455 /*! \brief Find an SLA station by name
03456  * \note This must be called with the sla_stations container locked
03457  */
03458 static struct sla_station *sla_find_station(const char *name)
03459 {
03460    struct sla_station *station = NULL;
03461 
03462    AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
03463       if (!strcasecmp(station->name, name))
03464          break;
03465    }
03466 
03467    return station;
03468 }
03469 
03470 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
03471    const struct sla_station *station)
03472 {
03473    struct sla_station_ref *station_ref;
03474    struct sla_trunk_ref *trunk_ref;
03475 
03476    /* For each station that has this call on hold, check for private hold. */
03477    AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
03478       AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
03479          if (trunk_ref->trunk != trunk || station_ref->station == station)
03480             continue;
03481          if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
03482             station_ref->station->hold_access == SLA_HOLD_PRIVATE)
03483             return 1;
03484          return 0;
03485       }
03486    }
03487 
03488    return 0;
03489 }
03490 
03491 /*! \brief Find a trunk reference on a station by name
03492  * \param station the station
03493  * \param name the trunk's name
03494  * \return a pointer to the station's trunk reference.  If the trunk
03495  *         is not found, it is not idle and barge is disabled, or if
03496  *         it is on hold and private hold is set, then NULL will be returned.
03497  */
03498 static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
03499    const char *name)
03500 {
03501    struct sla_trunk_ref *trunk_ref = NULL;
03502 
03503    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
03504       if (strcasecmp(trunk_ref->trunk->name, name))
03505          continue;
03506 
03507       if ( (trunk_ref->trunk->barge_disabled 
03508          && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
03509          (trunk_ref->trunk->hold_stations 
03510          && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
03511          && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
03512          sla_check_station_hold_access(trunk_ref->trunk, station) ) 
03513       {
03514          trunk_ref = NULL;
03515       }
03516 
03517       break;
03518    }
03519 
03520    return trunk_ref;
03521 }
03522 
03523 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
03524 {
03525    struct sla_station_ref *station_ref;
03526 
03527    if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
03528       return NULL;
03529 
03530    station_ref->station = station;
03531 
03532    return station_ref;
03533 }
03534 
03535 static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
03536 {
03537    struct sla_ringing_station *ringing_station;
03538 
03539    if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
03540       return NULL;
03541 
03542    ringing_station->station = station;
03543    ringing_station->ring_begin = ast_tvnow();
03544 
03545    return ringing_station;
03546 }
03547 
03548 static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state, 
03549    enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
03550 {
03551    struct sla_station *station;
03552    struct sla_trunk_ref *trunk_ref;
03553 
03554    AST_LIST_TRAVERSE(&sla_stations, station, entry) {
03555       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
03556          if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
03557             || trunk_ref == exclude)
03558             continue;
03559          trunk_ref->state = state;
03560          ast_device_state_changed("SLA:%s_%s", station->name, trunk->name);
03561          break;
03562       }
03563    }
03564 }
03565 
03566 struct run_station_args {
03567    struct sla_station *station;
03568    struct sla_trunk_ref *trunk_ref;
03569    ast_mutex_t *cond_lock;
03570    ast_cond_t *cond;
03571 };
03572 
03573 static void answer_trunk_chan(struct ast_channel *chan)
03574 {
03575    ast_answer(chan);
03576    ast_indicate(chan, -1);
03577 }
03578 
03579 static void *run_station(void *data)
03580 {
03581    struct sla_station *station;
03582    struct sla_trunk_ref *trunk_ref;
03583    char conf_name[MAX_CONFNUM];
03584    struct ast_flags conf_flags = { 0 };
03585    struct ast_conference *conf;
03586 
03587    {
03588       struct run_station_args *args = data;
03589       station = args->station;
03590       trunk_ref = args->trunk_ref;
03591       ast_mutex_lock(args->cond_lock);
03592       ast_cond_signal(args->cond);
03593       ast_mutex_unlock(args->cond_lock);
03594       /* args is no longer valid here. */
03595    }
03596 
03597    ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
03598    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
03599    ast_set_flag(&conf_flags, 
03600       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
03601    answer_trunk_chan(trunk_ref->chan);
03602    conf = build_conf(conf_name, "", "", 0, 0, 1);
03603    if (conf) {
03604       conf_run(trunk_ref->chan, conf, conf_flags.flags, NULL);
03605       dispose_conf(conf);
03606       conf = NULL;
03607    }
03608    trunk_ref->chan = NULL;
03609    if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
03610       trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
03611       strncat(conf_name, "|K", sizeof(conf_name) - strlen(conf_name) - 1);
03612       admin_exec(NULL, conf_name);
03613       trunk_ref->trunk->hold_stations = 0;
03614       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
03615    }
03616 
03617    ast_dial_join(station->dial);
03618    ast_dial_destroy(station->dial);
03619    station->dial = NULL;
03620 
03621    return NULL;
03622 }
03623 
03624 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
03625 {
03626    char buf[80];
03627    struct sla_station_ref *station_ref;
03628 
03629    snprintf(buf, sizeof(buf), "SLA_%s|K", ringing_trunk->trunk->name);
03630    admin_exec(NULL, buf);
03631    sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
03632 
03633    while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry)))
03634       free(station_ref);
03635 
03636    free(ringing_trunk);
03637 }
03638 
03639 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
03640    enum sla_station_hangup hangup)
03641 {
03642    struct sla_ringing_trunk *ringing_trunk;
03643    struct sla_trunk_ref *trunk_ref;
03644    struct sla_station_ref *station_ref;
03645 
03646    ast_dial_join(ringing_station->station->dial);
03647    ast_dial_destroy(ringing_station->station->dial);
03648    ringing_station->station->dial = NULL;
03649 
03650    if (hangup == SLA_STATION_HANGUP_NORMAL)
03651       goto done;
03652 
03653    /* If the station is being hung up because of a timeout, then add it to the
03654     * list of timed out stations on each of the ringing trunks.  This is so
03655     * that when doing further processing to figure out which stations should be
03656     * ringing, which trunk to answer, determining timeouts, etc., we know which
03657     * ringing trunks we should ignore. */
03658    AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
03659       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
03660          if (ringing_trunk->trunk == trunk_ref->trunk)
03661             break;
03662       }
03663       if (!trunk_ref)
03664          continue;
03665       if (!(station_ref = sla_create_station_ref(ringing_station->station)))
03666          continue;
03667       AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
03668    }
03669 
03670 done:
03671    free(ringing_station);
03672 }
03673 
03674 static void sla_dial_state_callback(struct ast_dial *dial)
03675 {
03676    sla_queue_event(SLA_EVENT_DIAL_STATE);
03677 }
03678 
03679 /*! \brief Check to see if dialing this station already timed out for this ringing trunk
03680  * \note Assumes sla.lock is locked
03681  */
03682 static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
03683    const struct sla_station *station)
03684 {
03685    struct sla_station_ref *timed_out_station;
03686 
03687    AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
03688       if (station == timed_out_station->station)
03689          return 1;
03690    }
03691 
03692    return 0;
03693 }
03694 
03695 /*! \brief Choose the highest priority ringing trunk for a station
03696  * \param station the station
03697  * \param remove remove the ringing trunk once selected
03698  * \param trunk_ref a place to store the pointer to this stations reference to
03699  *        the selected trunk
03700  * \return a pointer to the selected ringing trunk, or NULL if none found
03701  * \note Assumes that sla.lock is locked
03702  */
03703 static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station, 
03704    struct sla_trunk_ref **trunk_ref, int remove)
03705 {
03706    struct sla_trunk_ref *s_trunk_ref;
03707    struct sla_ringing_trunk *ringing_trunk = NULL;
03708 
03709    AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
03710       AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
03711          /* Make sure this is the trunk we're looking for */
03712          if (s_trunk_ref->trunk != ringing_trunk->trunk)
03713             continue;
03714 
03715          /* This trunk on the station is ringing.  But, make sure this station
03716           * didn't already time out while this trunk was ringing. */
03717          if (sla_check_timed_out_station(ringing_trunk, station))
03718             continue;
03719 
03720          if (remove)
03721             AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
03722 
03723          if (trunk_ref)
03724             *trunk_ref = s_trunk_ref;
03725 
03726          break;
03727       }
03728       AST_LIST_TRAVERSE_SAFE_END
03729    
03730       if (ringing_trunk)
03731          break;
03732    }
03733 
03734    return ringing_trunk;
03735 }
03736 
03737 static void sla_handle_dial_state_event(void)
03738 {
03739    struct sla_ringing_station *ringing_station;
03740 
03741    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
03742       struct sla_trunk_ref *s_trunk_ref = NULL;
03743       struct sla_ringing_trunk *ringing_trunk = NULL;
03744       struct run_station_args args;
03745       enum ast_dial_result dial_res;
03746       pthread_attr_t attr;
03747       pthread_t dont_care;
03748       ast_mutex_t cond_lock;
03749       ast_cond_t cond;
03750 
03751       switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
03752       case AST_DIAL_RESULT_HANGUP:
03753       case AST_DIAL_RESULT_INVALID:
03754       case AST_DIAL_RESULT_FAILED:
03755       case AST_DIAL_RESULT_TIMEOUT:
03756       case AST_DIAL_RESULT_UNANSWERED:
03757          AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
03758          sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
03759          break;
03760       case AST_DIAL_RESULT_ANSWERED:
03761          AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
03762          /* Find the appropriate trunk to answer. */
03763          ast_mutex_lock(&sla.lock);
03764          ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
03765          ast_mutex_unlock(&sla.lock);
03766          if (!ringing_trunk) {
03767             ast_log(LOG_DEBUG, "Found no ringing trunk for station '%s' to answer!\n",
03768                ringing_station->station->name);
03769             break;
03770          }
03771          /* Track the channel that answered this trunk */
03772          s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
03773          /* Actually answer the trunk */
03774          answer_trunk_chan(ringing_trunk->trunk->chan);
03775          sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
03776          /* Now, start a thread that will connect this station to the trunk.  The rest of
03777           * the code here sets up the thread and ensures that it is able to save the arguments
03778           * before they are no longer valid since they are allocated on the stack. */
03779          args.trunk_ref = s_trunk_ref;
03780          args.station = ringing_station->station;
03781          args.cond = &cond;
03782          args.cond_lock = &cond_lock;
03783          free(ringing_trunk);
03784          free(ringing_station);
03785          ast_mutex_init(&cond_lock);
03786          ast_cond_init(&cond, NULL);
03787          pthread_attr_init(&attr);
03788          pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
03789          ast_mutex_lock(&cond_lock);
03790          ast_pthread_create_background(&dont_care, &attr, run_station, &args);
03791          ast_cond_wait(&cond, &cond_lock);
03792          ast_mutex_unlock(&cond_lock);
03793          ast_mutex_destroy(&cond_lock);
03794          ast_cond_destroy(&cond);
03795          pthread_attr_destroy(&attr);
03796          break;
03797       case AST_DIAL_RESULT_TRYING:
03798       case AST_DIAL_RESULT_RINGING:
03799       case AST_DIAL_RESULT_PROGRESS:
03800       case AST_DIAL_RESULT_PROCEEDING:
03801          break;
03802       }
03803       if (dial_res == AST_DIAL_RESULT_ANSWERED) {
03804          /* Queue up reprocessing ringing trunks, and then ringing stations again */
03805          sla_queue_event(SLA_EVENT_RINGING_TRUNK);
03806          sla_queue_event(SLA_EVENT_DIAL_STATE);
03807          break;
03808       }
03809    }
03810    AST_LIST_TRAVERSE_SAFE_END
03811 }
03812 
03813 /*! \brief Check to see if this station is already ringing 
03814  * \note Assumes sla.lock is locked 
03815  */
03816 static int sla_check_ringing_station(const struct sla_station *station)
03817 {
03818    struct sla_ringing_station *ringing_station;
03819 
03820    AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
03821       if (station == ringing_station->station)
03822          return 1;
03823    }
03824 
03825    return 0;
03826 }
03827 
03828 /*! \brief Check to see if this station has failed to be dialed in the past minute
03829  * \note assumes sla.lock is locked
03830  */
03831 static int sla_check_failed_station(const struct sla_station *station)
03832 {
03833    struct sla_failed_station *failed_station;
03834    int res = 0;
03835 
03836    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
03837       if (station != failed_station->station)
03838          continue;
03839       if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
03840          AST_LIST_REMOVE_CURRENT(&sla.failed_stations, entry);
03841          free(failed_station);
03842          break;
03843       }
03844       res = 1;
03845    }
03846    AST_LIST_TRAVERSE_SAFE_END
03847 
03848    return res;
03849 }
03850 
03851 /*! \brief Ring a station
03852  * \note Assumes sla.lock is locked
03853  */
03854 static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
03855 {
03856    char *tech, *tech_data;
03857    struct ast_dial *dial;
03858    struct sla_ringing_station *ringing_station;
03859    const char *cid_name = NULL, *cid_num = NULL;
03860    enum ast_dial_result res;
03861 
03862    if (!(dial = ast_dial_create()))
03863       return -1;
03864 
03865    ast_dial_set_state_callback(dial, sla_dial_state_callback);
03866    tech_data = ast_strdupa(station->device);
03867    tech = strsep(&tech_data, "/");
03868 
03869    if (ast_dial_append(dial, tech, tech_data) == -1) {
03870       ast_dial_destroy(dial);
03871       return -1;
03872    }
03873 
03874    if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_name)) {
03875       cid_name = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_name);
03876       free(ringing_trunk->trunk->chan->cid.cid_name);
03877       ringing_trunk->trunk->chan->cid.cid_name = NULL;
03878    }
03879    if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_num)) {
03880       cid_num = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_num);
03881       free(ringing_trunk->trunk->chan->cid.cid_num);
03882       ringing_trunk->trunk->chan->cid.cid_num = NULL;
03883    }
03884 
03885    res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
03886    
03887    if (cid_name)
03888       ringing_trunk->trunk->chan->cid.cid_name = ast_strdup(cid_name);
03889    if (cid_num)
03890       ringing_trunk->trunk->chan->cid.cid_num = ast_strdup(cid_num);
03891    
03892    if (res != AST_DIAL_RESULT_TRYING) {
03893       struct sla_failed_station *failed_station;
03894       ast_dial_destroy(dial);
03895       if (!(failed_station = ast_calloc(1, sizeof(*failed_station))))
03896          return -1;
03897       failed_station->station = station;
03898       failed_station->last_try = ast_tvnow();
03899       AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
03900       return -1;
03901    }
03902    if (!(ringing_station = sla_create_ringing_station(station))) {
03903       ast_dial_join(dial);
03904       ast_dial_destroy(dial);
03905       return -1;
03906    }
03907 
03908    station->dial = dial;
03909 
03910    AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
03911 
03912    return 0;
03913 }
03914 
03915 /*! \brief Check to see if a station is in use
03916  */
03917 static int sla_check_inuse_station(const struct sla_station *station)
03918 {
03919    struct sla_trunk_ref *trunk_ref;
03920 
03921    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
03922       if (trunk_ref->chan)
03923          return 1;
03924    }
03925 
03926    return 0;
03927 }
03928 
03929 static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
03930    const struct sla_trunk *trunk)
03931 {
03932    struct sla_trunk_ref *trunk_ref = NULL;
03933 
03934    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
03935       if (trunk_ref->trunk == trunk)
03936          break;
03937    }
03938 
03939    return trunk_ref;
03940 }
03941 
03942 /*! \brief Calculate the ring delay for a given ringing trunk on a station
03943  * \param station the station
03944  * \param trunk the trunk.  If NULL, the highest priority ringing trunk will be used
03945  * \return the number of ms left before the delay is complete, or INT_MAX if there is no delay
03946  */
03947 static int sla_check_station_delay(struct sla_station *station, 
03948    struct sla_ringing_trunk *ringing_trunk)
03949 {
03950    struct sla_trunk_ref *trunk_ref;
03951    unsigned int delay = UINT_MAX;
03952    int time_left, time_elapsed;
03953 
03954    if (!ringing_trunk)
03955       ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
03956    else
03957       trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
03958 
03959    if (!ringing_trunk || !trunk_ref)
03960       return delay;
03961 
03962    /* If this station has a ring delay specific to the highest priority
03963     * ringing trunk, use that.  Otherwise, use the ring delay specified
03964     * globally for the station. */
03965    delay = trunk_ref->ring_delay;
03966    if (!delay)
03967       delay = station->ring_delay;
03968    if (!delay)
03969       return INT_MAX;
03970 
03971    time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
03972    time_left = (delay * 1000) - time_elapsed;
03973 
03974    return time_left;
03975 }
03976 
03977 /*! \brief Ring stations based on current set of ringing trunks
03978  * \note Assumes that sla.lock is locked
03979  */
03980 static void sla_ring_stations(void)
03981 {
03982    struct sla_station_ref *station_ref;
03983    struct sla_ringing_trunk *ringing_trunk;
03984 
03985    /* Make sure that every station that uses at least one of the ringing
03986     * trunks, is ringing. */
03987    AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
03988       AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
03989          int time_left;
03990 
03991          /* Is this station already ringing? */
03992          if (sla_check_ringing_station(station_ref->station))
03993             continue;
03994 
03995          /* Is this station already in a call? */
03996          if (sla_check_inuse_station(station_ref->station))
03997             continue;
03998 
03999          /* Did we fail to dial this station earlier?  If so, has it been
04000           * a minute since we tried? */
04001          if (sla_check_failed_station(station_ref->station))
04002             continue;
04003 
04004          /* If this station already timed out while this trunk was ringing,
04005           * do not dial it again for this ringing trunk. */
04006          if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
04007             continue;
04008 
04009          /* Check for a ring delay in progress */
04010          time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
04011          if (time_left != INT_MAX && time_left > 0)
04012             continue;
04013 
04014          /* It is time to make this station begin to ring.  Do it! */
04015          sla_ring_station(ringing_trunk, station_ref->station);
04016       }
04017    }
04018    /* Now, all of the stations that should be ringing, are ringing. */
04019 }
04020 
04021 static void sla_hangup_stations(void)
04022 {
04023    struct sla_trunk_ref *trunk_ref;
04024    struct sla_ringing_station *ringing_station;
04025 
04026    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
04027       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
04028          struct sla_ringing_trunk *ringing_trunk;
04029          ast_mutex_lock(&sla.lock);
04030          AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
04031             if (trunk_ref->trunk == ringing_trunk->trunk)
04032                break;
04033          }
04034          ast_mutex_unlock(&sla.lock);
04035          if (ringing_trunk)
04036             break;
04037       }
04038       if (!trunk_ref) {
04039          AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
04040          ast_dial_join(ringing_station->station->dial);
04041          ast_dial_destroy(ringing_station->station->dial);
04042          ringing_station->station->dial = NULL;
04043          free(ringing_station);
04044       }
04045    }
04046    AST_LIST_TRAVERSE_SAFE_END
04047 }
04048 
04049 static void sla_handle_ringing_trunk_event(void)
04050 {
04051    ast_mutex_lock(&sla.lock);
04052    sla_ring_stations();
04053    ast_mutex_unlock(&sla.lock);
04054 
04055    /* Find stations that shouldn't be ringing anymore. */
04056    sla_hangup_stations();
04057 }
04058 
04059 static void sla_handle_hold_event(struct sla_event *event)
04060 {
04061    ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
04062    event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
04063    ast_device_state_changed("SLA:%s_%s", 
04064       event->station->name, event->trunk_ref->trunk->name);
04065    sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD, 
04066       INACTIVE_TRUNK_REFS, event->trunk_ref);
04067 
04068    if (event->trunk_ref->trunk->active_stations == 1) {
04069       /* The station putting it on hold is the only one on the call, so start
04070        * Music on hold to the trunk. */
04071       event->trunk_ref->trunk->on_hold = 1;
04072       ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
04073    }
04074 
04075    ast_softhangup(event->trunk_ref->chan, AST_SOFTHANGUP_DEV);
04076    event->trunk_ref->chan = NULL;
04077 }
04078 
04079 /*! \brief Process trunk ring timeouts
04080  * \note Called with sla.lock locked
04081  * \return non-zero if a change to the ringing trunks was made
04082  */
04083 static int sla_calc_trunk_timeouts(unsigned int *timeout)
04084 {
04085    struct sla_ringing_trunk *ringing_trunk;
04086    int res = 0;
04087 
04088    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
04089       int time_left, time_elapsed;
04090       if (!ringing_trunk->trunk->ring_timeout)
04091          continue;
04092       time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
04093       time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
04094       if (time_left <= 0) {
04095          pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
04096          AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
04097          sla_stop_ringing_trunk(ringing_trunk);
04098          res = 1;
04099          continue;
04100       }
04101       if (time_left < *timeout)
04102          *timeout = time_left;
04103    }
04104    AST_LIST_TRAVERSE_SAFE_END
04105 
04106    return res;
04107 }
04108 
04109 /*! \brief Process station ring timeouts
04110  * \note Called with sla.lock locked
04111  * \return non-zero if a change to the ringing stations was made
04112  */
04113 static int sla_calc_station_timeouts(unsigned int *timeout)
04114 {
04115    struct sla_ringing_trunk *ringing_trunk;
04116    struct sla_ringing_station *ringing_station;
04117    int res = 0;
04118 
04119    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
04120       unsigned int ring_timeout = 0;
04121       int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
04122       struct sla_trunk_ref *trunk_ref;
04123 
04124       /* If there are any ring timeouts specified for a specific trunk
04125        * on the station, then use the highest per-trunk ring timeout.
04126        * Otherwise, use the ring timeout set for the entire station. */
04127       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
04128          struct sla_station_ref *station_ref;
04129          int trunk_time_elapsed, trunk_time_left;
04130 
04131          AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
04132             if (ringing_trunk->trunk == trunk_ref->trunk)
04133                break;
04134          }
04135          if (!ringing_trunk)
04136             continue;
04137 
04138          /* If there is a trunk that is ringing without a timeout, then the
04139           * only timeout that could matter is a global station ring timeout. */
04140          if (!trunk_ref->ring_timeout)
04141             break;
04142 
04143          /* This trunk on this station is ringing and has a timeout.
04144           * However, make sure this trunk isn't still ringing from a
04145           * previous timeout.  If so, don't consider it. */
04146          AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
04147             if (station_ref->station == ringing_station->station)
04148                break;
04149          }
04150          if (station_ref)
04151             continue;
04152 
04153          trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
04154          trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
04155          if (trunk_time_left > final_trunk_time_left)
04156             final_trunk_time_left = trunk_time_left;
04157       }
04158 
04159       /* No timeout was found for ringing trunks, and no timeout for the entire station */
04160       if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
04161          continue;
04162 
04163       /* Compute how much time is left for a global station timeout */
04164       if (ringing_station->station->ring_timeout) {
04165          ring_timeout = ringing_station->station->ring_timeout;
04166          time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
04167          time_left = (ring_timeout * 1000) - time_elapsed;
04168       }
04169 
04170       /* If the time left based on the per-trunk timeouts is smaller than the
04171        * global station ring timeout, use that. */
04172       if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
04173          time_left = final_trunk_time_left;
04174 
04175       /* If there is no time left, the station needs to stop ringing */
04176       if (time_left <= 0) {
04177          AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
04178          sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
04179          res = 1;
04180          continue;
04181       }
04182 
04183       /* There is still some time left for this station to ring, so save that
04184        * timeout if it is the first event scheduled to occur */
04185       if (time_left < *timeout)
04186          *timeout = time_left;
04187    }
04188    AST_LIST_TRAVERSE_SAFE_END
04189 
04190    return res;
04191 }
04192 
04193 /*! \brief Calculate the ring delay for a station
04194  * \note Assumes sla.lock is locked
04195  */
04196 static int sla_calc_station_delays(unsigned int *timeout)
04197 {
04198    struct sla_station *station;
04199    int res = 0;
04200 
04201    AST_LIST_TRAVERSE(&sla_stations, station, entry) {
04202       struct sla_ringing_trunk *ringing_trunk;
04203       int time_left;
04204 
04205       /* Ignore stations already ringing */
04206       if (sla_check_ringing_station(station))
04207          continue;
04208 
04209       /* Ignore stations already on a call */
04210       if (sla_check_inuse_station(station))
04211          continue;
04212 
04213       /* Ignore stations that don't have one of their trunks ringing */
04214       if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
04215          continue;
04216 
04217       if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
04218          continue;
04219 
04220       /* If there is no time left, then the station needs to start ringing.
04221        * Return non-zero so that an event will be queued up an event to 
04222        * make that happen. */
04223       if (time_left <= 0) {
04224          res = 1;
04225          continue;
04226       }
04227 
04228       if (time_left < *timeout)
04229          *timeout = time_left;
04230    }
04231 
04232    return res;
04233 }
04234 
04235 /*! \brief Calculate the time until the next known event
04236  *  \note Called with sla.lock locked */
04237 static int sla_process_timers(struct timespec *ts)
04238 {
04239    unsigned int timeout = UINT_MAX;
04240    struct timeval tv;
04241    unsigned int change_made = 0;
04242 
04243    /* Check for ring timeouts on ringing trunks */
04244    if (sla_calc_trunk_timeouts(&timeout))
04245       change_made = 1;
04246 
04247    /* Check for ring timeouts on ringing stations */
04248    if (sla_calc_station_timeouts(&timeout))
04249       change_made = 1;
04250 
04251    /* Check for station ring delays */
04252    if (sla_calc_station_delays(&timeout))
04253       change_made = 1;
04254 
04255    /* queue reprocessing of ringing trunks */
04256    if (change_made)
04257       sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
04258 
04259    /* No timeout */
04260    if (timeout == UINT_MAX)
04261       return 0;
04262 
04263    if (ts) {
04264       tv = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
04265       ts->tv_sec = tv.tv_sec;
04266       ts->tv_nsec = tv.tv_usec * 1000;
04267    }
04268 
04269    return 1;
04270 }
04271 
04272 static void *sla_thread(void *data)
04273 {
04274    struct sla_failed_station *failed_station;
04275    struct sla_ringing_station *ringing_station;
04276 
04277    ast_mutex_lock(&sla.lock);
04278 
04279    while (!sla.stop) {
04280       struct sla_event *event;
04281       struct timespec ts = { 0, };
04282       unsigned int have_timeout = 0;
04283 
04284       if (AST_LIST_EMPTY(&sla.event_q)) {
04285          if ((have_timeout = sla_process_timers(&ts)))
04286             ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
04287          else
04288             ast_cond_wait(&sla.cond, &sla.lock);
04289          if (sla.stop)
04290             break;
04291       }
04292 
04293       if (have_timeout)
04294          sla_process_timers(NULL);
04295 
04296       while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
04297          ast_mutex_unlock(&sla.lock);
04298          switch (event->type) {
04299          case SLA_EVENT_HOLD:
04300             sla_handle_hold_event(event);
04301             break;
04302          case SLA_EVENT_DIAL_STATE:
04303             sla_handle_dial_state_event();
04304             break;
04305          case SLA_EVENT_RINGING_TRUNK:
04306             sla_handle_ringing_trunk_event();
04307             break;
04308          }
04309          free(event);
04310          ast_mutex_lock(&sla.lock);
04311       }
04312    }
04313 
04314    ast_mutex_unlock(&sla.lock);
04315 
04316    while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry)))
04317       free(ringing_station);
04318 
04319    while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry)))
04320       free(failed_station);
04321 
04322    return NULL;
04323 }
04324 
04325 struct dial_trunk_args {
04326    struct sla_trunk_ref *trunk_ref;
04327    struct sla_station *station;
04328    ast_mutex_t *cond_lock;
04329    ast_cond_t *cond;
04330 };
04331 
04332 static void *dial_trunk(void *data)
04333 {
04334    struct dial_trunk_args *args = data;
04335    struct ast_dial *dial;
04336    char *tech, *tech_data;
04337    enum ast_dial_result dial_res;
04338    char conf_name[MAX_CONFNUM];
04339    struct ast_conference *conf;
04340    struct ast_flags conf_flags = { 0 };
04341    struct sla_trunk_ref *trunk_ref = args->trunk_ref;
04342    const char *cid_name = NULL, *cid_num = NULL;
04343 
04344    if (!(dial = ast_dial_create())) {
04345       ast_mutex_lock(args->cond_lock);
04346       ast_cond_signal(args->cond);
04347       ast_mutex_unlock(args->cond_lock);
04348       return NULL;
04349    }
04350 
04351    tech_data = ast_strdupa(trunk_ref->trunk->device);
04352    tech = strsep(&tech_data, "/");
04353    if (ast_dial_append(dial, tech, tech_data) == -1) {
04354       ast_mutex_lock(args->cond_lock);
04355       ast_cond_signal(args->cond);
04356       ast_mutex_unlock(args->cond_lock);
04357       ast_dial_destroy(dial);
04358       return NULL;
04359    }
04360 
04361    if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_name)) {
04362       cid_name = ast_strdupa(trunk_ref->chan->cid.cid_name);
04363       free(trunk_ref->chan->cid.cid_name);
04364       trunk_ref->chan->cid.cid_name = NULL;
04365    }
04366    if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_num)) {
04367       cid_num = ast_strdupa(trunk_ref->chan->cid.cid_num);
04368       free(trunk_ref->chan->cid.cid_num);
04369       trunk_ref->chan->cid.cid_num = NULL;
04370    }
04371 
04372    dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
04373 
04374    if (cid_name)
04375       trunk_ref->chan->cid.cid_name = ast_strdup(cid_name);
04376    if (cid_num)
04377       trunk_ref->chan->cid.cid_num = ast_strdup(cid_num);
04378 
04379    if (dial_res != AST_DIAL_RESULT_TRYING) {
04380       ast_mutex_lock(args->cond_lock);
04381       ast_cond_signal(args->cond);
04382       ast_mutex_unlock(args->cond_lock);
04383       ast_dial_destroy(dial);
04384       return NULL;
04385    }
04386 
04387    for (;;) {
04388       unsigned int done = 0;
04389       switch ((dial_res = ast_dial_state(dial))) {
04390       case AST_DIAL_RESULT_ANSWERED:
04391          trunk_ref->trunk->chan = ast_dial_answered(dial);
04392       case AST_DIAL_RESULT_HANGUP:
04393       case AST_DIAL_RESULT_INVALID:
04394       case AST_DIAL_RESULT_FAILED:
04395       case AST_DIAL_RESULT_TIMEOUT:
04396       case AST_DIAL_RESULT_UNANSWERED:
04397          done = 1;
04398       case AST_DIAL_RESULT_TRYING:
04399       case AST_DIAL_RESULT_RINGING:
04400       case AST_DIAL_RESULT_PROGRESS:
04401       case AST_DIAL_RESULT_PROCEEDING:
04402          break;
04403       }
04404       if (done)
04405          break;
04406    }
04407 
04408    if (!trunk_ref->trunk->chan) {
04409       ast_mutex_lock(args->cond_lock);
04410       ast_cond_signal(args->cond);
04411       ast_mutex_unlock(args->cond_lock);
04412       ast_dial_join(dial);
04413       ast_dial_destroy(dial);
04414       return NULL;
04415    }
04416 
04417    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
04418    ast_set_flag(&conf_flags, 
04419       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | 
04420       CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
04421    conf = build_conf(conf_name, "", "", 1, 1, 1);
04422 
04423    ast_mutex_lock(args->cond_lock);
04424    ast_cond_signal(args->cond);
04425    ast_mutex_unlock(args->cond_lock);
04426 
04427    if (conf) {
04428       conf_run(trunk_ref->trunk->chan, conf, conf_flags.flags, NULL);
04429       dispose_conf(conf);
04430       conf = NULL;
04431    }
04432 
04433    /* If the trunk is going away, it is definitely now IDLE. */
04434    sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
04435 
04436    trunk_ref->trunk->chan = NULL;
04437    trunk_ref->trunk->on_hold = 0;
04438 
04439    ast_dial_join(dial);
04440    ast_dial_destroy(dial);
04441 
04442    return NULL;
04443 }
04444 
04445 /*! \brief For a given station, choose the highest priority idle trunk
04446  */
04447 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
04448 {
04449    struct sla_trunk_ref *trunk_ref = NULL;
04450 
04451    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
04452       if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
04453          break;
04454    }
04455 
04456    return trunk_ref;
04457 }
04458 
04459 static int sla_station_exec(struct ast_channel *chan, void *data)
04460 {
04461    char *station_name, *trunk_name;
04462    struct sla_station *station;
04463    struct sla_trunk_ref *trunk_ref = NULL;
04464    char conf_name[MAX_CONFNUM];
04465    struct ast_flags conf_flags = { 0 };
04466    struct ast_conference *conf;
04467 
04468    if (ast_strlen_zero(data)) {
04469       ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
04470       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
04471       return 0;
04472    }
04473 
04474    trunk_name = ast_strdupa(data);
04475    station_name = strsep(&trunk_name, "_");
04476 
04477    if (ast_strlen_zero(station_name)) {
04478       ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
04479       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
04480       return 0;
04481    }
04482 
04483    AST_RWLIST_RDLOCK(&sla_stations);
04484    station = sla_find_station(station_name);
04485    AST_RWLIST_UNLOCK(&sla_stations);
04486 
04487    if (!station) {
04488       ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
04489       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
04490       return 0;
04491    }
04492 
04493    AST_RWLIST_RDLOCK(&sla_trunks);
04494    if (!ast_strlen_zero(trunk_name)) {
04495       trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
04496    } else
04497       trunk_ref = sla_choose_idle_trunk(station);
04498    AST_RWLIST_UNLOCK(&sla_trunks);
04499 
04500    if (!trunk_ref) {
04501       if (ast_strlen_zero(trunk_name))
04502          ast_log(LOG_NOTICE, "No trunks available for call.\n");
04503       else {
04504          ast_log(LOG_NOTICE, "Can't join existing call on trunk "
04505             "'%s' due to access controls.\n", trunk_name);
04506       }
04507       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
04508       return 0;
04509    }
04510 
04511    if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
04512       if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
04513          sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
04514       else {
04515          trunk_ref->state = SLA_TRUNK_STATE_UP;
04516          ast_device_state_changed("SLA:%s_%s", station->name, trunk_ref->trunk->name);
04517       }
04518    } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
04519       struct sla_ringing_trunk *ringing_trunk;
04520 
04521       ast_mutex_lock(&sla.lock);
04522       AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
04523          if (ringing_trunk->trunk == trunk_ref->trunk) {
04524             AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
04525             break;
04526          }
04527       }
04528       AST_LIST_TRAVERSE_SAFE_END
04529       ast_mutex_unlock(&sla.lock);
04530 
04531       if (ringing_trunk) {
04532          answer_trunk_chan(ringing_trunk->trunk->chan);
04533          sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
04534 
04535          free(ringing_trunk);
04536 
04537          /* Queue up reprocessing ringing trunks, and then ringing stations again */
04538          sla_queue_event(SLA_EVENT_RINGING_TRUNK);
04539          sla_queue_event(SLA_EVENT_DIAL_STATE);
04540       }
04541    }
04542 
04543    trunk_ref->chan = chan;
04544 
04545    if (!trunk_ref->trunk->chan) {
04546       ast_mutex_t cond_lock;
04547       ast_cond_t cond;
04548       pthread_t dont_care;
04549       pthread_attr_t attr;
04550       struct dial_trunk_args args = {
04551          .trunk_ref = trunk_ref,
04552          .station = station,
04553          .cond_lock = &cond_lock,
04554          .cond = &cond,
04555       };
04556       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
04557       /* Create a thread to dial the trunk and dump it into the conference.
04558        * However, we want to wait until the trunk has been dialed and the
04559        * conference is created before continuing on here. */
04560       ast_autoservice_start(chan);
04561       ast_mutex_init(&cond_lock);
04562       ast_cond_init(&cond, NULL);
04563       pthread_attr_init(&attr);
04564       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
04565       ast_mutex_lock(&cond_lock);
04566       ast_pthread_create_background(&dont_care, &attr, dial_trunk, &args);
04567       ast_cond_wait(&cond, &cond_lock);
04568       ast_mutex_unlock(&cond_lock);
04569       ast_mutex_destroy(&cond_lock);
04570       ast_cond_destroy(&cond);
04571       pthread_attr_destroy(&attr);
04572       ast_autoservice_stop(chan);
04573       if (!trunk_ref->trunk->chan) {
04574          ast_log(LOG_DEBUG, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
04575          pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
04576          sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
04577          trunk_ref->chan = NULL;
04578          return 0;
04579       }
04580    }
04581 
04582    if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
04583       trunk_ref->trunk->on_hold) {
04584       trunk_ref->trunk->on_hold = 0;
04585       ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
04586       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
04587    }
04588 
04589    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
04590    ast_set_flag(&conf_flags, 
04591       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
04592    ast_answer(chan);
04593    conf = build_conf(conf_name, "", "", 0, 0, 1);
04594    if (conf) {
04595       conf_run(chan, conf, conf_flags.flags, NULL);
04596       dispose_conf(conf);
04597       conf = NULL;
04598    }
04599    trunk_ref->chan = NULL;
04600    if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
04601       trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
04602       strncat(conf_name, "|K", sizeof(conf_name) - strlen(conf_name) - 1);
04603       admin_exec(NULL, conf_name);
04604       trunk_ref->trunk->hold_stations = 0;
04605       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
04606    }
04607    
04608    pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
04609 
04610    return 0;
04611 }
04612 
04613 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
04614 {
04615    struct sla_trunk_ref *trunk_ref;
04616 
04617    if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
04618       return NULL;
04619 
04620    trunk_ref->trunk = trunk;
04621 
04622    return trunk_ref;
04623 }
04624 
04625 static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
04626 {
04627    struct sla_ringing_trunk *ringing_trunk;
04628 
04629    if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
04630       return NULL;
04631    
04632    ringing_trunk->trunk = trunk;
04633    ringing_trunk->ring_begin = ast_tvnow();
04634 
04635    sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
04636 
04637    ast_mutex_lock(&sla.lock);
04638    AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
04639    ast_mutex_unlock(&sla.lock);
04640 
04641    sla_queue_event(SLA_EVENT_RINGING_TRUNK);
04642 
04643    return ringing_trunk;
04644 }
04645 
04646 static int sla_trunk_exec(struct ast_channel *chan, void *data)
04647 {
04648    const char *trunk_name = data;
04649    char conf_name[MAX_CONFNUM];
04650    struct ast_conference *conf;
04651    struct ast_flags conf_flags = { 0 };
04652    struct sla_trunk *trunk;
04653    struct sla_ringing_trunk *ringing_trunk;
04654 
04655    AST_RWLIST_RDLOCK(&sla_trunks);
04656    trunk = sla_find_trunk(trunk_name);
04657    AST_RWLIST_UNLOCK(&sla_trunks);
04658    if (!trunk) {
04659       ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", trunk_name);
04660       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
04661       return 0;
04662    }
04663    if (trunk->chan) {
04664       ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
04665          trunk_name);
04666       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
04667       return 0;
04668    }
04669    trunk->chan = chan;
04670 
04671    if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
04672       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
04673       return 0;
04674    }
04675 
04676    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_name);
04677    conf = build_conf(conf_name, "", "", 1, 1, 1);
04678    if (!conf) {
04679       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
04680       return 0;
04681    }
04682    ast_set_flag(&conf_flags, 
04683       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF | CONFFLAG_NO_AUDIO_UNTIL_UP);
04684    ast_indicate(chan, AST_CONTROL_RINGING);
04685    conf_run(chan, conf, conf_flags.flags, NULL);
04686    dispose_conf(conf);
04687    conf = NULL;
04688    trunk->chan = NULL;
04689    trunk->on_hold = 0;
04690 
04691    sla_change_trunk_state(trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
04692 
04693    if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
04694       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
04695 
04696    /* Remove the entry from the list of ringing trunks if it is still there. */
04697    ast_mutex_lock(&sla.lock);
04698    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
04699       if (ringing_trunk->trunk == trunk) {
04700          AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
04701          break;
04702       }
04703    }
04704    AST_LIST_TRAVERSE_SAFE_END
04705    ast_mutex_unlock(&sla.lock);
04706    if (ringing_trunk) {
04707       free(ringing_trunk);
04708       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
04709       /* Queue reprocessing of ringing trunks to make stations stop ringing
04710        * that shouldn't be ringing after this trunk stopped. */
04711       sla_queue_event(SLA_EVENT_RINGING_TRUNK);
04712    }
04713 
04714    return 0;
04715 }
04716 
04717 static int sla_state(const char *data)
04718 {
04719    char *buf, *station_name, *trunk_name;
04720    struct sla_station *station;
04721    struct sla_trunk_ref *trunk_ref;
04722    int res = AST_DEVICE_INVALID;
04723 
04724    trunk_name = buf = ast_strdupa(data);
04725    station_name = strsep(&trunk_name, "_");
04726 
04727    AST_RWLIST_RDLOCK(&sla_stations);
04728    AST_LIST_TRAVERSE(&sla_stations, station, entry) {
04729       if (strcasecmp(station_name, station->name))
04730          continue;
04731       AST_RWLIST_RDLOCK(&sla_trunks);
04732       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
04733          if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
04734             break;
04735       }
04736       if (!trunk_ref) {
04737          AST_RWLIST_UNLOCK(&sla_trunks);
04738          break;
04739       }
04740       switch (trunk_ref->state) {
04741       case SLA_TRUNK_STATE_IDLE:
04742          res = AST_DEVICE_NOT_INUSE;
04743          break;
04744       case SLA_TRUNK_STATE_RINGING:
04745          res = AST_DEVICE_RINGING;
04746          break;
04747       case SLA_TRUNK_STATE_UP:
04748          res = AST_DEVICE_INUSE;
04749          break;
04750       case SLA_TRUNK_STATE_ONHOLD:
04751       case SLA_TRUNK_STATE_ONHOLD_BYME:
04752          res = AST_DEVICE_ONHOLD;
04753          break;
04754       }
04755       AST_RWLIST_UNLOCK(&sla_trunks);
04756    }
04757    AST_RWLIST_UNLOCK(&sla_stations);
04758 
04759    if (res == AST_DEVICE_INVALID) {
04760       ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
04761          trunk_name, station_name);
04762    }
04763 
04764    return res;
04765 }
04766 
04767 static void destroy_trunk(struct sla_trunk *trunk)
04768 {
04769    struct sla_station_ref *station_ref;
04770 
04771    if (!ast_strlen_zero(trunk->autocontext))
04772       ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
04773 
04774    while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
04775       free(station_ref);
04776 
04777    ast_string_field_free_memory(trunk);
04778    free(trunk);
04779 }
04780 
04781 static void destroy_station(struct sla_station *station)
04782 {
04783    struct sla_trunk_ref *trunk_ref;
04784 
04785    if (!ast_strlen_zero(station->autocontext)) {
04786       AST_RWLIST_RDLOCK(&sla_trunks);
04787       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
04788          char exten[AST_MAX_EXTENSION];
04789          char hint[AST_MAX_APP];
04790          snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
04791          snprintf(hint, sizeof(hint), "SLA:%s", exten);
04792          ast_context_remove_extension(station->autocontext, exten, 
04793             1, sla_registrar);
04794          ast_context_remove_extension(station->autocontext, hint, 
04795             PRIORITY_HINT, sla_registrar);
04796       }
04797       AST_RWLIST_UNLOCK(&sla_trunks);
04798    }
04799 
04800    while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
04801       free(trunk_ref);
04802 
04803    ast_string_field_free_memory(station);
04804    free(station);
04805 }
04806 
04807 static void sla_destroy(void)
04808 {
04809    struct sla_trunk *trunk;
04810    struct sla_station *station;
04811 
04812    AST_RWLIST_WRLOCK(&sla_trunks);
04813    while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
04814       destroy_trunk(trunk);
04815    AST_RWLIST_UNLOCK(&sla_trunks);
04816 
04817    AST_RWLIST_WRLOCK(&sla_stations);
04818    while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
04819       destroy_station(station);
04820    AST_RWLIST_UNLOCK(&sla_stations);
04821 
04822    if (sla.thread != AST_PTHREADT_NULL) {
04823       ast_mutex_lock(&sla.lock);
04824       sla.stop = 1;
04825       ast_cond_signal(&sla.cond);
04826       ast_mutex_unlock(&sla.lock);
04827       pthread_join(sla.thread, NULL);
04828    }
04829 
04830    /* Drop any created contexts from the dialplan */
04831    ast_context_destroy(NULL, sla_registrar);
04832 
04833    ast_mutex_destroy(&sla.lock);
04834    ast_cond_destroy(&sla.cond);
04835 }
04836 
04837 static int sla_check_device(const char *device)
04838 {
04839    char *tech, *tech_data;
04840 
04841    tech_data = ast_strdupa(device);
04842    tech = strsep(&tech_data, "/");
04843 
04844    if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
04845       return -1;
04846 
04847    return 0;
04848 }
04849 
04850 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
04851 {
04852    struct sla_trunk *trunk;
04853    struct ast_variable *var;
04854    const char *dev;
04855 
04856    if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
04857       ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
04858       return -1;
04859    }
04860 
04861    if (sla_check_device(dev)) {
04862       ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
04863          cat, dev);
04864       return -1;
04865    }
04866 
04867    if (!(trunk = ast_calloc(1, sizeof(*trunk))))
04868       return -1;
04869    if (ast_string_field_init(trunk, 32)) {
04870       free(trunk);
04871       return -1;
04872    }
04873 
04874    ast_string_field_set(trunk, name, cat);
04875    ast_string_field_set(trunk, device, dev);
04876 
04877    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
04878       if (!strcasecmp(var->name, "autocontext"))
04879          ast_string_field_set(trunk, autocontext, var->value);
04880       else if (!strcasecmp(var->name, "ringtimeout")) {
04881          if (sscanf(var->value, "%30u", &trunk->ring_timeout) != 1) {
04882             ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
04883                var->value, trunk->name);
04884             trunk->ring_timeout = 0;
04885          }
04886       } else if (!strcasecmp(var->name, "barge"))
04887          trunk->barge_disabled = ast_false(var->value);
04888       else if (!strcasecmp(var->name, "hold")) {
04889          if (!strcasecmp(var->value, "private"))
04890             trunk->hold_access = SLA_HOLD_PRIVATE;
04891          else if (!strcasecmp(var->value, "open"))
04892             trunk->hold_access = SLA_HOLD_OPEN;
04893          else {
04894             ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
04895                var->value, trunk->name);
04896          }
04897       } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
04898          ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
04899             var->name, var->lineno, SLA_CONFIG_FILE);
04900       }
04901    }
04902 
04903    if (!ast_strlen_zero(trunk->autocontext)) {
04904       struct ast_context *context;
04905       context = ast_context_find_or_create(NULL, trunk->autocontext, sla_registrar);
04906       if (!context) {
04907          ast_log(LOG_ERROR, "Failed to automatically find or create "
04908             "context '%s' for SLA!\n", trunk->autocontext);
04909          destroy_trunk(trunk);
04910          return -1;
04911       }
04912       if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
04913          NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
04914          ast_log(LOG_ERROR, "Failed to automatically create extension "
04915             "for trunk '%s'!\n", trunk->name);
04916          destroy_trunk(trunk);
04917          return -1;
04918       }
04919    }
04920 
04921    AST_RWLIST_WRLOCK(&sla_trunks);
04922    AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
04923    AST_RWLIST_UNLOCK(&sla_trunks);
04924 
04925    return 0;
04926 }
04927 
04928 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
04929 {
04930    struct sla_trunk *trunk;
04931    struct sla_trunk_ref *trunk_ref;
04932    struct sla_station_ref *station_ref;
04933    char *trunk_name, *options, *cur;
04934 
04935    options = ast_strdupa(var->value);
04936    trunk_name = strsep(&options, ",");
04937    
04938    AST_RWLIST_RDLOCK(&sla_trunks);
04939    AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
04940       if (!strcasecmp(trunk->name, trunk_name))
04941          break;
04942    }
04943 
04944    AST_RWLIST_UNLOCK(&sla_trunks);
04945    if (!trunk) {
04946       ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
04947       return;
04948    }
04949    if (!(trunk_ref = create_trunk_ref(trunk)))
04950       return;
04951    trunk_ref->state = SLA_TRUNK_STATE_IDLE;
04952 
04953    while ((cur = strsep(&options, ","))) {
04954       char *name, *value = cur;
04955       name = strsep(&value, "=");
04956       if (!strcasecmp(name, "ringtimeout")) {
04957          if (sscanf(value, "%30u", &trunk_ref->ring_timeout) != 1) {
04958             ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
04959                "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
04960             trunk_ref->ring_timeout = 0;
04961          }
04962       } else if (!strcasecmp(name, "ringdelay")) {
04963          if (sscanf(value, "%30u", &trunk_ref->ring_delay) != 1) {
04964             ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
04965                "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
04966             trunk_ref->ring_delay = 0;
04967          }
04968       } else {
04969          ast_log(LOG_WARNING, "Invalid option '%s' for "
04970             "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
04971       }
04972    }
04973 
04974    if (!(station_ref = sla_create_station_ref(station))) {
04975       free(trunk_ref);
04976       return;
04977    }
04978    ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
04979    AST_RWLIST_WRLOCK(&sla_trunks);
04980    AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
04981    AST_RWLIST_UNLOCK(&sla_trunks);
04982    AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
04983 }
04984 
04985 static int sla_build_station(struct ast_config *cfg, const char *cat)
04986 {
04987    struct sla_station *station;
04988    struct ast_variable *var;
04989    const char *dev;
04990 
04991    if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
04992       ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
04993       return -1;
04994    }
04995 
04996    if (!(station = ast_calloc(1, sizeof(*station))))
04997       return -1;
04998    if (ast_string_field_init(station, 32)) {
04999       free(station);
05000       return -1;
05001    }
05002 
05003    ast_string_field_set(station, name, cat);
05004    ast_string_field_set(station, device, dev);
05005 
05006    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
05007       if (!strcasecmp(var->name, "trunk"))
05008          sla_add_trunk_to_station(station, var);
05009       else if (!strcasecmp(var->name, "autocontext"))
05010          ast_string_field_set(station, autocontext, var->value);
05011       else if (!strcasecmp(var->name, "ringtimeout")) {
05012          if (sscanf(var->value, "%30u", &station->ring_timeout) != 1) {
05013             ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
05014                var->value, station->name);
05015             station->ring_timeout = 0;
05016          }
05017       } else if (!strcasecmp(var->name, "ringdelay")) {
05018          if (sscanf(var->value, "%30u", &station->ring_delay) != 1) {
05019             ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
05020                var->value, station->name);
05021             station->ring_delay = 0;
05022          }
05023       } else if (!strcasecmp(var->name, "hold")) {
05024          if (!strcasecmp(var->value, "private"))
05025             station->hold_access = SLA_HOLD_PRIVATE;
05026          else if (!strcasecmp(var->value, "open"))
05027             station->hold_access = SLA_HOLD_OPEN;
05028          else {
05029             ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
05030                var->value, station->name);
05031          }
05032 
05033       } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
05034          ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
05035             var->name, var->lineno, SLA_CONFIG_FILE);
05036       }
05037    }
05038 
05039    if (!ast_strlen_zero(station->autocontext)) {
05040       struct ast_context *context;
05041       struct sla_trunk_ref *trunk_ref;
05042       context = ast_context_find_or_create(NULL, station->autocontext, sla_registrar);
05043       if (!context) {
05044          ast_log(LOG_ERROR, "Failed to automatically find or create "
05045             "context '%s' for SLA!\n", station->autocontext);
05046          destroy_station(station);
05047          return -1;
05048       }
05049       /* The extension for when the handset goes off-hook.
05050        * exten => station1,1,SLAStation(station1) */
05051       if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
05052          NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
05053          ast_log(LOG_ERROR, "Failed to automatically create extension "
05054             "for trunk '%s'!\n", station->name);
05055          destroy_station(station);
05056          return -1;
05057       }
05058       AST_RWLIST_RDLOCK(&sla_trunks);
05059       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05060          char exten[AST_MAX_EXTENSION];
05061          char hint[AST_MAX_APP];
05062          snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
05063          snprintf(hint, sizeof(hint), "SLA:%s", exten);
05064          /* Extension for this line button 
05065           * exten => station1_line1,1,SLAStation(station1_line1) */
05066          if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
05067             NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
05068             ast_log(LOG_ERROR, "Failed to automatically create extension "
05069                "for trunk '%s'!\n", station->name);
05070             destroy_station(station);
05071             return -1;
05072          }
05073          /* Hint for this line button 
05074           * exten => station1_line1,hint,SLA:station1_line1 */
05075          if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
05076             NULL, NULL, hint, NULL, NULL, sla_registrar)) {
05077             ast_log(LOG_ERROR, "Failed to automatically create hint "
05078                "for trunk '%s'!\n", station->name);
05079             destroy_station(station);
05080             return -1;
05081          }
05082       }
05083       AST_RWLIST_UNLOCK(&sla_trunks);
05084    }
05085 
05086    AST_RWLIST_WRLOCK(&sla_stations);
05087    AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
05088    AST_RWLIST_UNLOCK(&sla_stations);
05089 
05090    return 0;
05091 }
05092 
05093 static int sla_load_config(void)
05094 {
05095    struct ast_config *cfg;
05096    const char *cat = NULL;
05097    int res = 0;
05098    const char *val;
05099 
05100    ast_mutex_init(&sla.lock);
05101    ast_cond_init(&sla.cond, NULL);
05102 
05103    if (!(cfg = ast_config_load(SLA_CONFIG_FILE)))
05104       return 0; /* Treat no config as normal */
05105 
05106    if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
05107       sla.attempt_callerid = ast_true(val);
05108 
05109    while ((cat = ast_category_browse(cfg, cat)) && !res) {
05110       const char *type;
05111       if (!strcasecmp(cat, "general"))
05112          continue;
05113       if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
05114          ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
05115             SLA_CONFIG_FILE);
05116          continue;
05117       }
05118       if (!strcasecmp(type, "trunk"))
05119          res = sla_build_trunk(cfg, cat);
05120       else if (!strcasecmp(type, "station"))
05121          res = sla_build_station(cfg, cat);
05122       else {
05123          ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
05124             SLA_CONFIG_FILE, type);
05125       }
05126    }
05127 
05128    ast_config_destroy(cfg);
05129 
05130    if (!AST_LIST_EMPTY(&sla_stations) || !AST_LIST_EMPTY(&sla_stations))
05131       ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
05132 
05133    return res;
05134 }
05135 
05136 static int load_config(int reload)
05137 {
05138    int res = 0;
05139 
05140    load_config_meetme();
05141    if (!reload)
05142       res = sla_load_config();
05143 
05144    return res;
05145 }
05146 
05147 static int unload_module(void)
05148 {
05149    int res = 0;
05150    
05151    ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
05152    res = ast_manager_unregister("MeetmeMute");
05153    res |= ast_manager_unregister("MeetmeUnmute");
05154    res |= ast_unregister_application(app3);
05155    res |= ast_unregister_application(app2);
05156    res |= ast_unregister_application(app);
05157    res |= ast_unregister_application(slastation_app);
05158    res |= ast_unregister_application(slatrunk_app);
05159 
05160    ast_devstate_prov_del("Meetme");
05161    ast_devstate_prov_del("SLA");
05162 
05163    ast_module_user_hangup_all();
05164    
05165    sla_destroy();
05166 
05167    return res;
05168 }
05169 
05170 static int load_module(void)
05171 {
05172    int res = 0;
05173 
05174    res |= load_config(0);
05175 
05176    ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
05177    res |= ast_manager_register("MeetmeMute", EVENT_FLAG_CALL, 
05178                 action_meetmemute, "Mute a Meetme user");
05179    res |= ast_manager_register("MeetmeUnmute", EVENT_FLAG_CALL, 
05180                 action_meetmeunmute, "Unmute a Meetme user");
05181    res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
05182    res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
05183    res |= ast_register_application(app, conf_exec, synopsis, descrip);
05184    res |= ast_register_application(slastation_app, sla_station_exec,
05185                slastation_synopsis, slastation_desc);
05186    res |= ast_register_application(slatrunk_app, sla_trunk_exec,
05187                slatrunk_synopsis, slatrunk_desc);
05188 
05189    res |= ast_devstate_prov_add("Meetme", meetmestate);
05190    res |= ast_devstate_prov_add("SLA", sla_state);
05191 
05192    return res;
05193 }
05194 
05195 static int reload(void)
05196 {
05197    return load_config(1);
05198 }
05199 
05200 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MeetMe conference bridge",
05201       .load = load_module,
05202       .unload = unload_module,
05203       .reload = reload,
05204           );
05205 

Generated on Sat Aug 6 00:39:20 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7