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

Generated on Mon May 14 04:42:51 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1