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

Generated on Wed Aug 18 22:33:41 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7