Wed Mar 3 22:35:41 2010

Asterisk developer's documentation


app_meetme.c

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

Generated on 3 Mar 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1