Fri Sep 11 13:44:46 2009

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

Generated on Fri Sep 11 13:44:46 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7