Sat Apr 12 07:12:16 2008

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

Generated on Sat Apr 12 07:12:16 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.5