Wed Aug 15 01:24:13 2007

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

Generated on Wed Aug 15 01:24:14 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.3