Tue Apr 28 22:49:57 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: 179532 $")
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, "%d", &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, "%d", &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], "%d", &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                         if (can_write(chan, confflags) && ast_write(chan, conf->transframe[index]))
02356                            ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
02357                      }
02358                   } else {
02359                      ast_mutex_unlock(&conf->listenlock);
02360                      goto bailoutandtrynormal;
02361                   }
02362                   ast_mutex_unlock(&conf->listenlock);
02363                } else {
02364 bailoutandtrynormal:             
02365                   if (user->listen.actual)
02366                      ast_frame_adjust_volume(&fr, user->listen.actual);
02367                   if (can_write(chan, confflags) && ast_write(chan, &fr) < 0) {
02368                      ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
02369                   }
02370                }
02371             } else 
02372                ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
02373          }
02374          lastmarked = currentmarked;
02375       }
02376    }
02377 
02378    if (musiconhold)
02379       ast_moh_stop(chan);
02380    
02381    if (using_pseudo)
02382       close(fd);
02383    else {
02384       /* Take out of conference */
02385       ztc.chan = 0;  
02386       ztc.confno = 0;
02387       ztc.confmode = 0;
02388       if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
02389          ast_log(LOG_WARNING, "Error setting conference\n");
02390       }
02391    }
02392 
02393    reset_volumes(user);
02394 
02395    if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
02396       conf_play(chan, conf, LEAVE);
02397 
02398    if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
02399       struct announce_listitem *item;
02400       if (!(item = ao2_alloc(sizeof(*item), NULL)))
02401          return -1;
02402       ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
02403       ast_copy_string(item->language, chan->language, sizeof(item->language));
02404       item->confchan = conf->chan;
02405       item->confusers = conf->users;
02406       item->announcetype = CONF_HASLEFT;
02407       ast_mutex_lock(&conf->announcelistlock);
02408       AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
02409       ast_cond_signal(&conf->announcelist_addition);
02410       ast_mutex_unlock(&conf->announcelistlock);
02411    } else if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users == 1) {
02412       /* Last person is leaving, so no reason to try and announce, but should delete the name recording */
02413       ast_filedelete(user->namerecloc, NULL);
02414    }
02415 
02416  outrun:
02417    AST_LIST_LOCK(&confs);
02418 
02419    if (dsp)
02420       ast_dsp_free(dsp);
02421    
02422    if (user->user_no) { /* Only cleanup users who really joined! */
02423       now = time(NULL);
02424       hr = (now - user->jointime) / 3600;
02425       min = ((now - user->jointime) % 3600) / 60;
02426       sec = (now - user->jointime) % 60;
02427 
02428       if (sent_event) {
02429          manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
02430                   "Channel: %s\r\n"
02431                   "Uniqueid: %s\r\n"
02432                   "Meetme: %s\r\n"
02433                   "Usernum: %d\r\n"
02434                   "CallerIDnum: %s\r\n"
02435                   "CallerIDname: %s\r\n"
02436                   "Duration: %ld\r\n",
02437                   chan->name, chan->uniqueid, conf->confno, 
02438                   user->user_no,
02439                   S_OR(user->chan->cid.cid_num, "<unknown>"),
02440                   S_OR(user->chan->cid.cid_name, "<unknown>"),
02441                   (long)(now - user->jointime));
02442       }
02443 
02444       if (setusercount) {
02445          conf->users--;
02446          /* Update table */
02447          snprintf(members, sizeof(members), "%d", conf->users);
02448          ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
02449          if (confflags & CONFFLAG_MARKEDUSER) 
02450             conf->markedusers--;
02451       }
02452       /* Remove ourselves from the list */
02453       AST_LIST_REMOVE(&conf->userlist, user, list);
02454 
02455       /* Change any states */
02456       if (!conf->users)
02457          ast_device_state_changed("meetme:%s", conf->confno);
02458       
02459       /* Return the number of seconds the user was in the conf */
02460       snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
02461       pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
02462    }
02463    free(user);
02464    AST_LIST_UNLOCK(&confs);
02465 
02466    return ret;
02467 }
02468 
02469 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
02470                    char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
02471 {
02472    struct ast_variable *var, *save;
02473    struct ast_conference *cnf;
02474 
02475    /* Check first in the conference list */
02476    AST_LIST_LOCK(&confs);
02477    AST_LIST_TRAVERSE(&confs, cnf, list) {
02478       if (!strcmp(confno, cnf->confno)) 
02479          break;
02480    }
02481    if (cnf) {
02482       cnf->refcount += refcount;
02483    }
02484    AST_LIST_UNLOCK(&confs);
02485 
02486    if (!cnf) {
02487       char *pin = NULL, *pinadmin = NULL; /* For temp use */
02488       
02489       var = ast_load_realtime("meetme", "confno", confno, NULL);
02490 
02491       if (!var)
02492          return NULL;
02493 
02494       save = var;
02495       while (var) {
02496          if (!strcasecmp(var->name, "pin")) {
02497             pin = ast_strdupa(var->value);
02498          } else if (!strcasecmp(var->name, "adminpin")) {
02499             pinadmin = ast_strdupa(var->value);
02500          }
02501          var = var->next;
02502       }
02503       ast_variables_destroy(save);
02504       
02505       cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount);
02506    }
02507 
02508    if (cnf) {
02509       if (confflags && !cnf->chan &&
02510           !ast_test_flag(confflags, CONFFLAG_QUIET) &&
02511           ast_test_flag(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW)) {
02512          ast_log(LOG_WARNING, "No %s channel available for conference, user introduction disabled\n", dahdi_chan_name);
02513          ast_clear_flag(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW);
02514       }
02515       
02516       if (confflags && !cnf->chan &&
02517           ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
02518          ast_log(LOG_WARNING, "No %s channel available for conference, conference recording disabled\n", dahdi_chan_name);
02519          ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
02520       }
02521    }
02522 
02523    return cnf;
02524 }
02525 
02526 
02527 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
02528                char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
02529 {
02530    struct ast_config *cfg;
02531    struct ast_variable *var;
02532    struct ast_conference *cnf;
02533    char *parse;
02534    AST_DECLARE_APP_ARGS(args,
02535       AST_APP_ARG(confno);
02536       AST_APP_ARG(pin);
02537       AST_APP_ARG(pinadmin);
02538    );
02539 
02540    /* Check first in the conference list */
02541    AST_LIST_LOCK(&confs);
02542    AST_LIST_TRAVERSE(&confs, cnf, list) {
02543       if (!strcmp(confno, cnf->confno)) 
02544          break;
02545    }
02546    if (cnf){
02547       cnf->refcount += refcount;
02548    }
02549    AST_LIST_UNLOCK(&confs);
02550 
02551    if (!cnf) {
02552       if (dynamic) {
02553          /* No need to parse meetme.conf */
02554          ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
02555          if (dynamic_pin) {
02556             if (dynamic_pin[0] == 'q') {
02557                /* Query the user to enter a PIN */
02558                if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
02559                   return NULL;
02560             }
02561             cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount);
02562          } else {
02563             cnf = build_conf(confno, "", "", make, dynamic, refcount);
02564          }
02565       } else {
02566          /* Check the config */
02567          cfg = ast_config_load(CONFIG_FILE_NAME);
02568          if (!cfg) {
02569             ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
02570             return NULL;
02571          }
02572          for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
02573             if (strcasecmp(var->name, "conf"))
02574                continue;
02575             
02576             if (!(parse = ast_strdupa(var->value)))
02577                return NULL;
02578             
02579             AST_NONSTANDARD_APP_ARGS(args, parse, ',');
02580             if (!strcasecmp(args.confno, confno)) {
02581                /* Bingo it's a valid conference */
02582                cnf = build_conf(args.confno,
02583                      S_OR(args.pin, ""),
02584                      S_OR(args.pinadmin, ""),
02585                      make, dynamic, refcount);
02586                break;
02587             }
02588          }
02589          if (!var) {
02590             ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
02591          }
02592          ast_config_destroy(cfg);
02593       }
02594    } else if (dynamic_pin) {
02595       /* Correct for the user selecting 'D' instead of 'd' to have
02596          someone join into a conference that has already been created
02597          with a pin. */
02598       if (dynamic_pin[0] == 'q')
02599          dynamic_pin[0] = '\0';
02600    }
02601 
02602    if (cnf) {
02603       if (confflags && !cnf->chan &&
02604           !ast_test_flag(confflags, CONFFLAG_QUIET) &&
02605           ast_test_flag(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW)) {
02606          ast_log(LOG_WARNING, "No %s channel available for conference, user introduction disabled\n", dahdi_chan_name);
02607          ast_clear_flag(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW);
02608       }
02609       
02610       if (confflags && !cnf->chan &&
02611           ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
02612          ast_log(LOG_WARNING, "No %s channel available for conference, conference recording disabled\n", dahdi_chan_name);
02613          ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
02614       }
02615    }
02616 
02617    return cnf;
02618 }
02619 
02620 /*! \brief The MeetmeCount application */
02621 static int count_exec(struct ast_channel *chan, void *data)
02622 {
02623    struct ast_module_user *u;
02624    int res = 0;
02625    struct ast_conference *conf;
02626    int count;
02627    char *localdata;
02628    char val[80] = "0"; 
02629    AST_DECLARE_APP_ARGS(args,
02630       AST_APP_ARG(confno);
02631       AST_APP_ARG(varname);
02632    );
02633 
02634    if (ast_strlen_zero(data)) {
02635       ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
02636       return -1;
02637    }
02638 
02639    u = ast_module_user_add(chan);
02640    
02641    if (!(localdata = ast_strdupa(data))) {
02642       ast_module_user_remove(u);
02643       return -1;
02644    }
02645 
02646    AST_STANDARD_APP_ARGS(args, localdata);
02647    
02648    conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
02649 
02650    if (conf) {
02651       count = conf->users;
02652       dispose_conf(conf);
02653       conf = NULL;
02654    } else
02655       count = 0;
02656 
02657    if (!ast_strlen_zero(args.varname)){
02658       /* have var so load it and exit */
02659       snprintf(val, sizeof(val), "%d",count);
02660       pbx_builtin_setvar_helper(chan, args.varname, val);
02661    } else {
02662       if (chan->_state != AST_STATE_UP)
02663          ast_answer(chan);
02664       res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
02665    }
02666    ast_module_user_remove(u);
02667 
02668    return res;
02669 }
02670 
02671 /*! \brief The meetme() application */
02672 static int conf_exec(struct ast_channel *chan, void *data)
02673 {
02674    int res=-1;
02675    struct ast_module_user *u;
02676    char confno[MAX_CONFNUM] = "";
02677    int allowretry = 0;
02678    int retrycnt = 0;
02679    struct ast_conference *cnf = NULL;
02680    struct ast_flags confflags = {0};
02681    int dynamic = 0;
02682    int empty = 0, empty_no_pin = 0;
02683    int always_prompt = 0;
02684    char *notdata, *info, the_pin[MAX_PIN] = "";
02685    AST_DECLARE_APP_ARGS(args,
02686       AST_APP_ARG(confno);
02687       AST_APP_ARG(options);
02688       AST_APP_ARG(pin);
02689    );
02690    char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
02691 
02692    u = ast_module_user_add(chan);
02693 
02694    if (ast_strlen_zero(data)) {
02695       allowretry = 1;
02696       notdata = "";
02697    } else {
02698       notdata = data;
02699    }
02700    
02701    if (chan->_state != AST_STATE_UP)
02702       ast_answer(chan);
02703 
02704    info = ast_strdupa(notdata);
02705 
02706    AST_STANDARD_APP_ARGS(args, info);  
02707 
02708    if (args.confno) {
02709       ast_copy_string(confno, args.confno, sizeof(confno));
02710       if (ast_strlen_zero(confno)) {
02711          allowretry = 1;
02712       }
02713    }
02714    
02715    if (args.pin)
02716       ast_copy_string(the_pin, args.pin, sizeof(the_pin));
02717 
02718    if (args.options) {
02719       ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
02720       dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
02721       if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && ast_strlen_zero(args.pin))
02722          strcpy(the_pin, "q");
02723 
02724       empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
02725       empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
02726       always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
02727    }
02728 
02729    do {
02730       if (retrycnt > 3)
02731          allowretry = 0;
02732       if (empty) {
02733          int i;
02734          struct ast_config *cfg;
02735          struct ast_variable *var;
02736          int confno_int;
02737 
02738          /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
02739          if ((empty_no_pin) || (!dynamic)) {
02740             cfg = ast_config_load(CONFIG_FILE_NAME);
02741             if (cfg) {
02742                var = ast_variable_browse(cfg, "rooms");
02743                while (var) {
02744                   if (!strcasecmp(var->name, "conf")) {
02745                      char *stringp = ast_strdupa(var->value);
02746                      if (stringp) {
02747                         char *confno_tmp = strsep(&stringp, "|,");
02748                         int found = 0;
02749                         if (!dynamic) {
02750                            /* For static:  run through the list and see if this conference is empty */
02751                            AST_LIST_LOCK(&confs);
02752                            AST_LIST_TRAVERSE(&confs, cnf, list) {
02753                               if (!strcmp(confno_tmp, cnf->confno)) {
02754                                  /* The conference exists, therefore it's not empty */
02755                                  found = 1;
02756                                  break;
02757                               }
02758                            }
02759                            AST_LIST_UNLOCK(&confs);
02760                            if (!found) {
02761                               /* At this point, we have a confno_tmp (static conference) that is empty */
02762                               if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
02763                                  /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
02764                                   * Case 2:  empty_no_pin and pin is blank (but not NULL)
02765                                   * Case 3:  not empty_no_pin
02766                                   */
02767                                  ast_copy_string(confno, confno_tmp, sizeof(confno));
02768                                  break;
02769                                  /* XXX the map is not complete (but we do have a confno) */
02770                               }
02771                            }
02772                         }
02773                      }
02774                   }
02775                   var = var->next;
02776                }
02777                ast_config_destroy(cfg);
02778             }
02779          }
02780 
02781          /* Select first conference number not in use */
02782          if (ast_strlen_zero(confno) && dynamic) {
02783             AST_LIST_LOCK(&confs);
02784             for (i = 0; i < sizeof(conf_map) / sizeof(conf_map[0]); i++) {
02785                if (!conf_map[i]) {
02786                   snprintf(confno, sizeof(confno), "%d", i);
02787                   conf_map[i] = 1;
02788                   break;
02789                }
02790             }
02791             AST_LIST_UNLOCK(&confs);
02792          }
02793 
02794          /* Not found? */
02795          if (ast_strlen_zero(confno)) {
02796             res = ast_streamfile(chan, "conf-noempty", chan->language);
02797             if (!res)
02798                ast_waitstream(chan, "");
02799          } else {
02800             if (sscanf(confno, "%d", &confno_int) == 1) {
02801                res = ast_streamfile(chan, "conf-enteringno", chan->language);
02802                if (!res) {
02803                   ast_waitstream(chan, "");
02804                   res = ast_say_digits(chan, confno_int, "", chan->language);
02805                }
02806             } else {
02807                ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
02808             }
02809          }
02810       }
02811 
02812       while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
02813          /* Prompt user for conference number */
02814          res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
02815          if (res < 0) {
02816             /* Don't try to validate when we catch an error */
02817             confno[0] = '\0';
02818             allowretry = 0;
02819             break;
02820          }
02821       }
02822       if (!ast_strlen_zero(confno)) {
02823          /* Check the validity of the conference */
02824          cnf = find_conf(chan, confno, 1, dynamic, the_pin, 
02825             sizeof(the_pin), 1, &confflags);
02826          if (!cnf) {
02827             cnf = find_conf_realtime(chan, confno, 1, dynamic, 
02828                the_pin, sizeof(the_pin), 1, &confflags);
02829          }
02830 
02831          if (!cnf) {
02832             res = ast_streamfile(chan, "conf-invalid", chan->language);
02833             if (!res)
02834                ast_waitstream(chan, "");
02835             res = -1;
02836             if (allowretry)
02837                confno[0] = '\0';
02838          } else {
02839             if ((!ast_strlen_zero(cnf->pin) &&
02840                  !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
02841                 (!ast_strlen_zero(cnf->pinadmin) &&
02842                  ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
02843                char pin[MAX_PIN] = "";
02844                int j;
02845 
02846                /* Allow the pin to be retried up to 3 times */
02847                for (j = 0; j < 3; j++) {
02848                   if (*the_pin && (always_prompt == 0)) {
02849                      ast_copy_string(pin, the_pin, sizeof(pin));
02850                      res = 0;
02851                   } else {
02852                      /* Prompt user for pin if pin is required */
02853                      res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
02854                   }
02855                   if (res >= 0) {
02856                      if (!strcasecmp(pin, cnf->pin) ||
02857                          (!ast_strlen_zero(cnf->pinadmin) &&
02858                           !strcasecmp(pin, cnf->pinadmin))) {
02859                         /* Pin correct */
02860                         allowretry = 0;
02861                         if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) 
02862                            ast_set_flag(&confflags, CONFFLAG_ADMIN);
02863                         /* Run the conference */
02864                         res = conf_run(chan, cnf, confflags.flags, optargs);
02865                         break;
02866                      } else {
02867                         /* Pin invalid */
02868                         if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
02869                            res = ast_waitstream(chan, AST_DIGIT_ANY);
02870                            ast_stopstream(chan);
02871                         }
02872                         else {
02873                            ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
02874                            break;
02875                         }
02876                         if (res < 0)
02877                            break;
02878                         pin[0] = res;
02879                         pin[1] = '\0';
02880                         res = -1;
02881                         if (allowretry)
02882                            confno[0] = '\0';
02883                      }
02884                   } else {
02885                      /* failed when getting the pin */
02886                      res = -1;
02887                      allowretry = 0;
02888                      /* see if we need to get rid of the conference */
02889                      break;
02890                   }
02891 
02892                   /* Don't retry pin with a static pin */
02893                   if (*the_pin && (always_prompt==0)) {
02894                      break;
02895                   }
02896                }
02897             } else {
02898                /* No pin required */
02899                allowretry = 0;
02900 
02901                /* Run the conference */
02902                res = conf_run(chan, cnf, confflags.flags, optargs);
02903             }
02904             dispose_conf(cnf);
02905             cnf = NULL;
02906          }
02907       }
02908    } while (allowretry);
02909 
02910    if (cnf)
02911       dispose_conf(cnf);
02912 
02913    ast_module_user_remove(u);
02914    
02915    return res;
02916 }
02917 
02918 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident) 
02919 {
02920    struct ast_conf_user *user = NULL;
02921    int cid;
02922    
02923    sscanf(callerident, "%i", &cid);
02924    if (conf && callerident) {
02925       AST_LIST_TRAVERSE(&conf->userlist, user, list) {
02926          if (cid == user->user_no)
02927             return user;
02928       }
02929    }
02930    return NULL;
02931 }
02932 
02933 /*! \brief The MeetMeadmin application */
02934 /* MeetMeAdmin(confno, command, caller) */
02935 static int admin_exec(struct ast_channel *chan, void *data) {
02936    char *params;
02937    struct ast_conference *cnf;
02938    struct ast_conf_user *user = NULL;
02939    struct ast_module_user *u;
02940    AST_DECLARE_APP_ARGS(args,
02941       AST_APP_ARG(confno);
02942       AST_APP_ARG(command);
02943       AST_APP_ARG(user);
02944    );
02945 
02946    if (ast_strlen_zero(data)) {
02947       ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
02948       return -1;
02949    }
02950 
02951    u = ast_module_user_add(chan);
02952 
02953    AST_LIST_LOCK(&confs);
02954    
02955    params = ast_strdupa(data);
02956    AST_STANDARD_APP_ARGS(args, params);
02957 
02958    if (!args.command) {
02959       ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
02960       AST_LIST_UNLOCK(&confs);
02961       ast_module_user_remove(u);
02962       return -1;
02963    }
02964    AST_LIST_TRAVERSE(&confs, cnf, list) {
02965       if (!strcmp(cnf->confno, args.confno))
02966          break;
02967    }
02968 
02969    if (!cnf) {
02970       ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
02971       AST_LIST_UNLOCK(&confs);
02972       ast_module_user_remove(u);
02973       return 0;
02974    }
02975 
02976    ast_atomic_fetchadd_int(&cnf->refcount, 1);
02977 
02978    if (args.user)
02979       user = find_user(cnf, args.user);
02980 
02981    switch (*args.command) {
02982    case 76: /* L: Lock */ 
02983       cnf->locked = 1;
02984       break;
02985    case 108: /* l: Unlock */ 
02986       cnf->locked = 0;
02987       break;
02988    case 75: /* K: kick all users */
02989       AST_LIST_TRAVERSE(&cnf->userlist, user, list)
02990          user->adminflags |= ADMINFLAG_KICKME;
02991       break;
02992    case 101: /* e: Eject last user*/
02993       user = AST_LIST_LAST(&cnf->userlist);
02994       if (!(user->userflags & CONFFLAG_ADMIN))
02995          user->adminflags |= ADMINFLAG_KICKME;
02996       else
02997          ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
02998       break;
02999    case 77: /* M: Mute */ 
03000       if (user) {
03001          user->adminflags |= ADMINFLAG_MUTED;
03002       } else
03003          ast_log(LOG_NOTICE, "Specified User not found!\n");
03004       break;
03005    case 78: /* N: Mute all (non-admin) users */
03006       AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
03007          if (!(user->userflags & CONFFLAG_ADMIN))
03008             user->adminflags |= ADMINFLAG_MUTED;
03009       }
03010       break;               
03011    case 109: /* m: Unmute */ 
03012       if (user) {
03013          user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
03014       } else
03015          ast_log(LOG_NOTICE, "Specified User not found!\n");
03016       break;
03017    case 110: /* n: Unmute all users */
03018       AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03019          user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
03020       break;
03021    case 107: /* k: Kick user */ 
03022       if (user)
03023          user->adminflags |= ADMINFLAG_KICKME;
03024       else
03025          ast_log(LOG_NOTICE, "Specified User not found!\n");
03026       break;
03027    case 118: /* v: Lower all users listen volume */
03028       AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03029          tweak_listen_volume(user, VOL_DOWN);
03030       break;
03031    case 86: /* V: Raise all users listen volume */
03032       AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03033          tweak_listen_volume(user, VOL_UP);
03034       break;
03035    case 115: /* s: Lower all users speaking volume */
03036       AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03037          tweak_talk_volume(user, VOL_DOWN);
03038       break;
03039    case 83: /* S: Raise all users speaking volume */
03040       AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03041          tweak_talk_volume(user, VOL_UP);
03042       break;
03043    case 82: /* R: Reset all volume levels */
03044       AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03045          reset_volumes(user);
03046       break;
03047    case 114: /* r: Reset user's volume level */
03048       if (user)
03049          reset_volumes(user);
03050       else
03051          ast_log(LOG_NOTICE, "Specified User not found!\n");
03052       break;
03053    case 85: /* U: Raise user's listen volume */
03054       if (user)
03055          tweak_listen_volume(user, VOL_UP);
03056       else
03057          ast_log(LOG_NOTICE, "Specified User not found!\n");
03058       break;
03059    case 117: /* u: Lower user's listen volume */
03060       if (user)
03061          tweak_listen_volume(user, VOL_DOWN);
03062       else
03063          ast_log(LOG_NOTICE, "Specified User not found!\n");
03064       break;
03065    case 84: /* T: Raise user's talk volume */
03066       if (user)
03067          tweak_talk_volume(user, VOL_UP);
03068       else
03069          ast_log(LOG_NOTICE, "Specified User not found!\n");
03070       break;
03071    case 116: /* t: Lower user's talk volume */
03072       if (user) 
03073          tweak_talk_volume(user, VOL_DOWN);
03074       else 
03075          ast_log(LOG_NOTICE, "Specified User not found!\n");
03076       break;
03077    }
03078 
03079    AST_LIST_UNLOCK(&confs);
03080 
03081    dispose_conf(cnf);
03082 
03083    ast_module_user_remove(u);
03084    
03085    return 0;
03086 }
03087 
03088 static int meetmemute(struct mansession *s, const struct message *m, int mute)
03089 {
03090    struct ast_conference *conf;
03091    struct ast_conf_user *user;
03092    const char *confid = astman_get_header(m, "Meetme");
03093    char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
03094    int userno;
03095 
03096    if (ast_strlen_zero(confid)) {
03097       astman_send_error(s, m, "Meetme conference not specified");
03098       return 0;
03099    }
03100 
03101    if (ast_strlen_zero(userid)) {
03102       astman_send_error(s, m, "Meetme user number not specified");
03103       return 0;
03104    }
03105 
03106    userno = strtoul(userid, &userid, 10);
03107 
03108    if (*userid) {
03109       astman_send_error(s, m, "Invalid user number");
03110       return 0;
03111    }
03112 
03113    /* Look in the conference list */
03114    AST_LIST_LOCK(&confs);
03115    AST_LIST_TRAVERSE(&confs, conf, list) {
03116       if (!strcmp(confid, conf->confno))
03117          break;
03118    }
03119 
03120    if (!conf) {
03121       AST_LIST_UNLOCK(&confs);
03122       astman_send_error(s, m, "Meetme conference does not exist");
03123       return 0;
03124    }
03125 
03126    AST_LIST_TRAVERSE(&conf->userlist, user, list)
03127       if (user->user_no == userno)
03128          break;
03129 
03130    if (!user) {
03131       AST_LIST_UNLOCK(&confs);
03132       astman_send_error(s, m, "User number not found");
03133       return 0;
03134    }
03135 
03136    if (mute)
03137       user->adminflags |= ADMINFLAG_MUTED;   /* request user muting */
03138    else
03139       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);  /* request user unmuting */
03140 
03141    AST_LIST_UNLOCK(&confs);
03142 
03143    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);
03144 
03145    astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
03146    return 0;
03147 }
03148 
03149 static int action_meetmemute(struct mansession *s, const struct message *m)
03150 {
03151    return meetmemute(s, m, 1);
03152 }
03153 
03154 static int action_meetmeunmute(struct mansession *s, const struct message *m)
03155 {
03156    return meetmemute(s, m, 0);
03157 }
03158 
03159 static void *recordthread(void *args)
03160 {
03161    struct ast_conference *cnf = args;
03162    struct ast_frame *f=NULL;
03163    int flags;
03164    struct ast_filestream *s=NULL;
03165    int res=0;
03166    int x;
03167    const char *oldrecordingfilename = NULL;
03168 
03169    if (!cnf || !cnf->lchan) {
03170       pthread_exit(0);
03171    }
03172 
03173    ast_stopstream(cnf->lchan);
03174    flags = O_CREAT|O_TRUNC|O_WRONLY;
03175 
03176 
03177    cnf->recording = MEETME_RECORD_ACTIVE;
03178    while (ast_waitfor(cnf->lchan, -1) > -1) {
03179       if (cnf->recording == MEETME_RECORD_TERMINATE) {
03180          AST_LIST_LOCK(&confs);
03181          AST_LIST_UNLOCK(&confs);
03182          break;
03183       }
03184       if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
03185          s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
03186          oldrecordingfilename = cnf->recordingfilename;
03187       }
03188       
03189       f = ast_read(cnf->lchan);
03190       if (!f) {
03191          res = -1;
03192          break;
03193       }
03194       if (f->frametype == AST_FRAME_VOICE) {
03195          ast_mutex_lock(&cnf->listenlock);
03196          for (x=0;x<AST_FRAME_BITS;x++) {
03197             /* Free any translations that have occured */
03198             if (cnf->transframe[x]) {
03199                ast_frfree(cnf->transframe[x]);
03200                cnf->transframe[x] = NULL;
03201             }
03202          }
03203          if (cnf->origframe)
03204             ast_frfree(cnf->origframe);
03205          cnf->origframe = ast_frdup(f);
03206          ast_mutex_unlock(&cnf->listenlock);
03207          if (s)
03208             res = ast_writestream(s, f);
03209          if (res) {
03210             ast_frfree(f);
03211             break;
03212          }
03213       }
03214       ast_frfree(f);
03215    }
03216    cnf->recording = MEETME_RECORD_OFF;
03217    if (s)
03218       ast_closestream(s);
03219    
03220    pthread_exit(0);
03221 }
03222 
03223 /*! \brief Callback for devicestate providers */
03224 static int meetmestate(const char *data)
03225 {
03226    struct ast_conference *conf;
03227 
03228    /* Find conference */
03229    AST_LIST_LOCK(&confs);
03230    AST_LIST_TRAVERSE(&confs, conf, list) {
03231       if (!strcmp(data, conf->confno))
03232          break;
03233    }
03234    AST_LIST_UNLOCK(&confs);
03235    if (!conf)
03236       return AST_DEVICE_INVALID;
03237 
03238 
03239    /* SKREP to fill */
03240    if (!conf->users)
03241       return AST_DEVICE_NOT_INUSE;
03242 
03243    return AST_DEVICE_INUSE;
03244 }
03245 
03246 static void load_config_meetme(void)
03247 {
03248    struct ast_config *cfg;
03249    const char *val;
03250 
03251    audio_buffers = DEFAULT_AUDIO_BUFFERS;
03252 
03253    if (!(cfg = ast_config_load(CONFIG_FILE_NAME)))
03254       return;
03255 
03256    if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
03257       if ((sscanf(val, "%d", &audio_buffers) != 1)) {
03258          ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
03259          audio_buffers = DEFAULT_AUDIO_BUFFERS;
03260       } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
03261          ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
03262             DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
03263          audio_buffers = DEFAULT_AUDIO_BUFFERS;
03264       }
03265       if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
03266          ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
03267    }
03268 
03269    ast_config_destroy(cfg);
03270 }
03271 
03272 /*! \brief Find an SLA trunk by name
03273  * \note This must be called with the sla_trunks container locked
03274  */
03275 static struct sla_trunk *sla_find_trunk(const char *name)
03276 {
03277    struct sla_trunk *trunk = NULL;
03278 
03279    AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
03280       if (!strcasecmp(trunk->name, name))
03281          break;
03282    }
03283 
03284    return trunk;
03285 }
03286 
03287 /*! \brief Find an SLA station by name
03288  * \note This must be called with the sla_stations container locked
03289  */
03290 static struct sla_station *sla_find_station(const char *name)
03291 {
03292    struct sla_station *station = NULL;
03293 
03294    AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
03295       if (!strcasecmp(station->name, name))
03296          break;
03297    }
03298 
03299    return station;
03300 }
03301 
03302 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
03303    const struct sla_station *station)
03304 {
03305    struct sla_station_ref *station_ref;
03306    struct sla_trunk_ref *trunk_ref;
03307 
03308    /* For each station that has this call on hold, check for private hold. */
03309    AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
03310       AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
03311          if (trunk_ref->trunk != trunk || station_ref->station == station)
03312             continue;
03313          if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
03314             station_ref->station->hold_access == SLA_HOLD_PRIVATE)
03315             return 1;
03316          return 0;
03317       }
03318    }
03319 
03320    return 0;
03321 }
03322 
03323 /*! \brief Find a trunk reference on a station by name
03324  * \param station the station
03325  * \param name the trunk's name
03326  * \return a pointer to the station's trunk reference.  If the trunk
03327  *         is not found, it is not idle and barge is disabled, or if
03328  *         it is on hold and private hold is set, then NULL will be returned.
03329  */
03330 static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
03331    const char *name)
03332 {
03333    struct sla_trunk_ref *trunk_ref = NULL;
03334 
03335    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
03336       if (strcasecmp(trunk_ref->trunk->name, name))
03337          continue;
03338 
03339       if ( (trunk_ref->trunk->barge_disabled 
03340          && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
03341          (trunk_ref->trunk->hold_stations 
03342          && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
03343          && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
03344          sla_check_station_hold_access(trunk_ref->trunk, station) ) 
03345       {
03346          trunk_ref = NULL;
03347       }
03348 
03349       break;
03350    }
03351 
03352    return trunk_ref;
03353 }
03354 
03355 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
03356 {
03357    struct sla_station_ref *station_ref;
03358 
03359    if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
03360       return NULL;
03361 
03362    station_ref->station = station;
03363 
03364    return station_ref;
03365 }
03366 
03367 static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
03368 {
03369    struct sla_ringing_station *ringing_station;
03370 
03371    if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
03372       return NULL;
03373 
03374    ringing_station->station = station;
03375    ringing_station->ring_begin = ast_tvnow();
03376 
03377    return ringing_station;
03378 }
03379 
03380 static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state, 
03381    enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
03382 {
03383    struct sla_station *station;
03384    struct sla_trunk_ref *trunk_ref;
03385 
03386    AST_LIST_TRAVERSE(&sla_stations, station, entry) {
03387       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
03388          if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
03389             || trunk_ref == exclude)
03390             continue;
03391          trunk_ref->state = state;
03392          ast_device_state_changed("SLA:%s_%s", station->name, trunk->name);
03393          break;
03394       }
03395    }
03396 }
03397 
03398 struct run_station_args {
03399    struct sla_station *station;
03400    struct sla_trunk_ref *trunk_ref;
03401    ast_mutex_t *cond_lock;
03402    ast_cond_t *cond;
03403 };
03404 
03405 static void answer_trunk_chan(struct ast_channel *chan)
03406 {
03407    ast_answer(chan);
03408    ast_indicate(chan, -1);
03409 }
03410 
03411 static void *run_station(void *data)
03412 {
03413    struct sla_station *station;
03414    struct sla_trunk_ref *trunk_ref;
03415    char conf_name[MAX_CONFNUM];
03416    struct ast_flags conf_flags = { 0 };
03417    struct ast_conference *conf;
03418 
03419    {
03420       struct run_station_args *args = data;
03421       station = args->station;
03422       trunk_ref = args->trunk_ref;
03423       ast_mutex_lock(args->cond_lock);
03424       ast_cond_signal(args->cond);
03425       ast_mutex_unlock(args->cond_lock);
03426       /* args is no longer valid here. */
03427    }
03428 
03429    ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
03430    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
03431    ast_set_flag(&conf_flags, 
03432       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
03433    answer_trunk_chan(trunk_ref->chan);
03434    conf = build_conf(conf_name, "", "", 0, 0, 1);
03435    if (conf) {
03436       conf_run(trunk_ref->chan, conf, conf_flags.flags, NULL);
03437       dispose_conf(conf);
03438       conf = NULL;
03439    }
03440    trunk_ref->chan = NULL;
03441    if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
03442       trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
03443       strncat(conf_name, "|K", sizeof(conf_name) - strlen(conf_name) - 1);
03444       admin_exec(NULL, conf_name);
03445       trunk_ref->trunk->hold_stations = 0;
03446       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
03447    }
03448 
03449    ast_dial_join(station->dial);
03450    ast_dial_destroy(station->dial);
03451    station->dial = NULL;
03452 
03453    return NULL;
03454 }
03455 
03456 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
03457 {
03458    char buf[80];
03459    struct sla_station_ref *station_ref;
03460 
03461    snprintf(buf, sizeof(buf), "SLA_%s|K", ringing_trunk->trunk->name);
03462    admin_exec(NULL, buf);
03463    sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
03464 
03465    while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry)))
03466       free(station_ref);
03467 
03468    free(ringing_trunk);
03469 }
03470 
03471 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
03472    enum sla_station_hangup hangup)
03473 {
03474    struct sla_ringing_trunk *ringing_trunk;
03475    struct sla_trunk_ref *trunk_ref;
03476    struct sla_station_ref *station_ref;
03477 
03478    ast_dial_join(ringing_station->station->dial);
03479    ast_dial_destroy(ringing_station->station->dial);
03480    ringing_station->station->dial = NULL;
03481 
03482    if (hangup == SLA_STATION_HANGUP_NORMAL)
03483       goto done;
03484 
03485    /* If the station is being hung up because of a timeout, then add it to the
03486     * list of timed out stations on each of the ringing trunks.  This is so
03487     * that when doing further processing to figure out which stations should be
03488     * ringing, which trunk to answer, determining timeouts, etc., we know which
03489     * ringing trunks we should ignore. */
03490    AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
03491       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
03492          if (ringing_trunk->trunk == trunk_ref->trunk)
03493             break;
03494       }
03495       if (!trunk_ref)
03496          continue;
03497       if (!(station_ref = sla_create_station_ref(ringing_station->station)))
03498          continue;
03499       AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
03500    }
03501 
03502 done:
03503    free(ringing_station);
03504 }
03505 
03506 static void sla_dial_state_callback(struct ast_dial *dial)
03507 {
03508    sla_queue_event(SLA_EVENT_DIAL_STATE);
03509 }
03510 
03511 /*! \brief Check to see if dialing this station already timed out for this ringing trunk
03512  * \note Assumes sla.lock is locked
03513  */
03514 static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
03515    const struct sla_station *station)
03516 {
03517    struct sla_station_ref *timed_out_station;
03518 
03519    AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
03520       if (station == timed_out_station->station)
03521          return 1;
03522    }
03523 
03524    return 0;
03525 }
03526 
03527 /*! \brief Choose the highest priority ringing trunk for a station
03528  * \param station the station
03529  * \param remove remove the ringing trunk once selected
03530  * \param trunk_ref a place to store the pointer to this stations reference to
03531  *        the selected trunk
03532  * \return a pointer to the selected ringing trunk, or NULL if none found
03533  * \note Assumes that sla.lock is locked
03534  */
03535 static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station, 
03536    struct sla_trunk_ref **trunk_ref, int remove)
03537 {
03538    struct sla_trunk_ref *s_trunk_ref;
03539    struct sla_ringing_trunk *ringing_trunk = NULL;
03540 
03541    AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
03542       AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
03543          /* Make sure this is the trunk we're looking for */
03544          if (s_trunk_ref->trunk != ringing_trunk->trunk)
03545             continue;
03546 
03547          /* This trunk on the station is ringing.  But, make sure this station
03548           * didn't already time out while this trunk was ringing. */
03549          if (sla_check_timed_out_station(ringing_trunk, station))
03550             continue;
03551 
03552          if (remove)
03553             AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
03554 
03555          if (trunk_ref)
03556             *trunk_ref = s_trunk_ref;
03557 
03558          break;
03559       }
03560       AST_LIST_TRAVERSE_SAFE_END
03561    
03562       if (ringing_trunk)
03563          break;
03564    }
03565 
03566    return ringing_trunk;
03567 }
03568 
03569 static void sla_handle_dial_state_event(void)
03570 {
03571    struct sla_ringing_station *ringing_station;
03572 
03573    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
03574       struct sla_trunk_ref *s_trunk_ref = NULL;
03575       struct sla_ringing_trunk *ringing_trunk = NULL;
03576       struct run_station_args args;
03577       enum ast_dial_result dial_res;
03578       pthread_attr_t attr;
03579       pthread_t dont_care;
03580       ast_mutex_t cond_lock;
03581       ast_cond_t cond;
03582 
03583       switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
03584       case AST_DIAL_RESULT_HANGUP:
03585       case AST_DIAL_RESULT_INVALID:
03586       case AST_DIAL_RESULT_FAILED:
03587       case AST_DIAL_RESULT_TIMEOUT:
03588       case AST_DIAL_RESULT_UNANSWERED:
03589          AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
03590          sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
03591          break;
03592       case AST_DIAL_RESULT_ANSWERED:
03593          AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
03594          /* Find the appropriate trunk to answer. */
03595          ast_mutex_lock(&sla.lock);
03596          ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
03597          ast_mutex_unlock(&sla.lock);
03598          if (!ringing_trunk) {
03599             ast_log(LOG_DEBUG, "Found no ringing trunk for station '%s' to answer!\n",
03600                ringing_station->station->name);
03601             break;
03602          }
03603          /* Track the channel that answered this trunk */
03604          s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
03605          /* Actually answer the trunk */
03606          answer_trunk_chan(ringing_trunk->trunk->chan);
03607          sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
03608          /* Now, start a thread that will connect this station to the trunk.  The rest of
03609           * the code here sets up the thread and ensures that it is able to save the arguments
03610           * before they are no longer valid since they are allocated on the stack. */
03611          args.trunk_ref = s_trunk_ref;
03612          args.station = ringing_station->station;
03613          args.cond = &cond;
03614          args.cond_lock = &cond_lock;
03615          free(ringing_trunk);
03616          free(ringing_station);
03617          ast_mutex_init(&cond_lock);
03618          ast_cond_init(&cond, NULL);
03619          pthread_attr_init(&attr);
03620          pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
03621          ast_mutex_lock(&cond_lock);
03622          ast_pthread_create_background(&dont_care, &attr, run_station, &args);
03623          ast_cond_wait(&cond, &cond_lock);
03624          ast_mutex_unlock(&cond_lock);
03625          ast_mutex_destroy(&cond_lock);
03626          ast_cond_destroy(&cond);
03627          pthread_attr_destroy(&attr);
03628          break;
03629       case AST_DIAL_RESULT_TRYING:
03630       case AST_DIAL_RESULT_RINGING:
03631       case AST_DIAL_RESULT_PROGRESS:
03632       case AST_DIAL_RESULT_PROCEEDING:
03633          break;
03634       }
03635       if (dial_res == AST_DIAL_RESULT_ANSWERED) {
03636          /* Queue up reprocessing ringing trunks, and then ringing stations again */
03637          sla_queue_event(SLA_EVENT_RINGING_TRUNK);
03638          sla_queue_event(SLA_EVENT_DIAL_STATE);
03639          break;
03640       }
03641    }
03642    AST_LIST_TRAVERSE_SAFE_END
03643 }
03644 
03645 /*! \brief Check to see if this station is already ringing 
03646  * \note Assumes sla.lock is locked 
03647  */
03648 static int sla_check_ringing_station(const struct sla_station *station)
03649 {
03650    struct sla_ringing_station *ringing_station;
03651 
03652    AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
03653       if (station == ringing_station->station)
03654          return 1;
03655    }
03656 
03657    return 0;
03658 }
03659 
03660 /*! \brief Check to see if this station has failed to be dialed in the past minute
03661  * \note assumes sla.lock is locked
03662  */
03663 static int sla_check_failed_station(const struct sla_station *station)
03664 {
03665    struct sla_failed_station *failed_station;
03666    int res = 0;
03667 
03668    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
03669       if (station != failed_station->station)
03670          continue;
03671       if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
03672          AST_LIST_REMOVE_CURRENT(&sla.failed_stations, entry);
03673          free(failed_station);
03674          break;
03675       }
03676       res = 1;
03677    }
03678    AST_LIST_TRAVERSE_SAFE_END
03679 
03680    return res;
03681 }
03682 
03683 /*! \brief Ring a station
03684  * \note Assumes sla.lock is locked
03685  */
03686 static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
03687 {
03688    char *tech, *tech_data;
03689    struct ast_dial *dial;
03690    struct sla_ringing_station *ringing_station;
03691    const char *cid_name = NULL, *cid_num = NULL;
03692    enum ast_dial_result res;
03693 
03694    if (!(dial = ast_dial_create()))
03695       return -1;
03696 
03697    ast_dial_set_state_callback(dial, sla_dial_state_callback);
03698    tech_data = ast_strdupa(station->device);
03699    tech = strsep(&tech_data, "/");
03700 
03701    if (ast_dial_append(dial, tech, tech_data) == -1) {
03702       ast_dial_destroy(dial);
03703       return -1;
03704    }
03705 
03706    if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_name)) {
03707       cid_name = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_name);
03708       free(ringing_trunk->trunk->chan->cid.cid_name);
03709       ringing_trunk->trunk->chan->cid.cid_name = NULL;
03710    }
03711    if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_num)) {
03712       cid_num = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_num);
03713       free(ringing_trunk->trunk->chan->cid.cid_num);
03714       ringing_trunk->trunk->chan->cid.cid_num = NULL;
03715    }
03716 
03717    res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
03718    
03719    if (cid_name)
03720       ringing_trunk->trunk->chan->cid.cid_name = ast_strdup(cid_name);
03721    if (cid_num)
03722       ringing_trunk->trunk->chan->cid.cid_num = ast_strdup(cid_num);
03723    
03724    if (res != AST_DIAL_RESULT_TRYING) {
03725       struct sla_failed_station *failed_station;
03726       ast_dial_destroy(dial);
03727       if (!(failed_station = ast_calloc(1, sizeof(*failed_station))))
03728          return -1;
03729       failed_station->station = station;
03730       failed_station->last_try = ast_tvnow();
03731       AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
03732       return -1;
03733    }
03734    if (!(ringing_station = sla_create_ringing_station(station))) {
03735       ast_dial_join(dial);
03736       ast_dial_destroy(dial);
03737       return -1;
03738    }
03739 
03740    station->dial = dial;
03741 
03742    AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
03743 
03744    return 0;
03745 }
03746 
03747 /*! \brief Check to see if a station is in use
03748  */
03749 static int sla_check_inuse_station(const struct sla_station *station)
03750 {
03751    struct sla_trunk_ref *trunk_ref;
03752 
03753    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
03754       if (trunk_ref->chan)
03755          return 1;
03756    }
03757 
03758    return 0;
03759 }
03760 
03761 static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
03762    const struct sla_trunk *trunk)
03763 {
03764    struct sla_trunk_ref *trunk_ref = NULL;
03765 
03766    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
03767       if (trunk_ref->trunk == trunk)
03768          break;
03769    }
03770 
03771    return trunk_ref;
03772 }
03773 
03774 /*! \brief Calculate the ring delay for a given ringing trunk on a station
03775  * \param station the station
03776  * \param trunk the trunk.  If NULL, the highest priority ringing trunk will be used
03777  * \return the number of ms left before the delay is complete, or INT_MAX if there is no delay
03778  */
03779 static int sla_check_station_delay(struct sla_station *station, 
03780    struct sla_ringing_trunk *ringing_trunk)
03781 {
03782    struct sla_trunk_ref *trunk_ref;
03783    unsigned int delay = UINT_MAX;
03784    int time_left, time_elapsed;
03785 
03786    if (!ringing_trunk)
03787       ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
03788    else
03789       trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
03790 
03791    if (!ringing_trunk || !trunk_ref)
03792       return delay;
03793 
03794    /* If this station has a ring delay specific to the highest priority
03795     * ringing trunk, use that.  Otherwise, use the ring delay specified
03796     * globally for the station. */
03797    delay = trunk_ref->ring_delay;
03798    if (!delay)
03799       delay = station->ring_delay;
03800    if (!delay)
03801       return INT_MAX;
03802 
03803    time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
03804    time_left = (delay * 1000) - time_elapsed;
03805 
03806    return time_left;
03807 }
03808 
03809 /*! \brief Ring stations based on current set of ringing trunks
03810  * \note Assumes that sla.lock is locked
03811  */
03812 static void sla_ring_stations(void)
03813 {
03814    struct sla_station_ref *station_ref;
03815    struct sla_ringing_trunk *ringing_trunk;
03816 
03817    /* Make sure that every station that uses at least one of the ringing
03818     * trunks, is ringing. */
03819    AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
03820       AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
03821          int time_left;
03822 
03823          /* Is this station already ringing? */
03824          if (sla_check_ringing_station(station_ref->station))
03825             continue;
03826 
03827          /* Is this station already in a call? */
03828          if (sla_check_inuse_station(station_ref->station))
03829             continue;
03830 
03831          /* Did we fail to dial this station earlier?  If so, has it been
03832           * a minute since we tried? */
03833          if (sla_check_failed_station(station_ref->station))
03834             continue;
03835 
03836          /* If this station already timed out while this trunk was ringing,
03837           * do not dial it again for this ringing trunk. */
03838          if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
03839             continue;
03840 
03841          /* Check for a ring delay in progress */
03842          time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
03843          if (time_left != INT_MAX && time_left > 0)
03844             continue;
03845 
03846          /* It is time to make this station begin to ring.  Do it! */
03847          sla_ring_station(ringing_trunk, station_ref->station);
03848       }
03849    }
03850    /* Now, all of the stations that should be ringing, are ringing. */
03851 }
03852 
03853 static void sla_hangup_stations(void)
03854 {
03855    struct sla_trunk_ref *trunk_ref;
03856    struct sla_ringing_station *ringing_station;
03857 
03858    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
03859       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
03860          struct sla_ringing_trunk *ringing_trunk;
03861          ast_mutex_lock(&sla.lock);
03862          AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
03863             if (trunk_ref->trunk == ringing_trunk->trunk)
03864                break;
03865          }
03866          ast_mutex_unlock(&sla.lock);
03867          if (ringing_trunk)
03868             break;
03869       }
03870       if (!trunk_ref) {
03871          AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
03872          ast_dial_join(ringing_station->station->dial);
03873          ast_dial_destroy(ringing_station->station->dial);
03874          ringing_station->station->dial = NULL;
03875          free(ringing_station);
03876       }
03877    }
03878    AST_LIST_TRAVERSE_SAFE_END
03879 }
03880 
03881 static void sla_handle_ringing_trunk_event(void)
03882 {
03883    ast_mutex_lock(&sla.lock);
03884    sla_ring_stations();
03885    ast_mutex_unlock(&sla.lock);
03886 
03887    /* Find stations that shouldn't be ringing anymore. */
03888    sla_hangup_stations();
03889 }
03890 
03891 static void sla_handle_hold_event(struct sla_event *event)
03892 {
03893    ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
03894    event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
03895    ast_device_state_changed("SLA:%s_%s", 
03896       event->station->name, event->trunk_ref->trunk->name);
03897    sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD, 
03898       INACTIVE_TRUNK_REFS, event->trunk_ref);
03899 
03900    if (event->trunk_ref->trunk->active_stations == 1) {
03901       /* The station putting it on hold is the only one on the call, so start
03902        * Music on hold to the trunk. */
03903       event->trunk_ref->trunk->on_hold = 1;
03904       ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
03905    }
03906 
03907    ast_softhangup(event->trunk_ref->chan, AST_CAUSE_NORMAL);
03908    event->trunk_ref->chan = NULL;
03909 }
03910 
03911 /*! \brief Process trunk ring timeouts
03912  * \note Called with sla.lock locked
03913  * \return non-zero if a change to the ringing trunks was made
03914  */
03915 static int sla_calc_trunk_timeouts(unsigned int *timeout)
03916 {
03917    struct sla_ringing_trunk *ringing_trunk;
03918    int res = 0;
03919 
03920    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
03921       int time_left, time_elapsed;
03922       if (!ringing_trunk->trunk->ring_timeout)
03923          continue;
03924       time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
03925       time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
03926       if (time_left <= 0) {
03927          pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
03928          AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
03929          sla_stop_ringing_trunk(ringing_trunk);
03930          res = 1;
03931          continue;
03932       }
03933       if (time_left < *timeout)
03934          *timeout = time_left;
03935    }
03936    AST_LIST_TRAVERSE_SAFE_END
03937 
03938    return res;
03939 }
03940 
03941 /*! \brief Process station ring timeouts
03942  * \note Called with sla.lock locked
03943  * \return non-zero if a change to the ringing stations was made
03944  */
03945 static int sla_calc_station_timeouts(unsigned int *timeout)
03946 {
03947    struct sla_ringing_trunk *ringing_trunk;
03948    struct sla_ringing_station *ringing_station;
03949    int res = 0;
03950 
03951    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
03952       unsigned int ring_timeout = 0;
03953       int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
03954       struct sla_trunk_ref *trunk_ref;
03955 
03956       /* If there are any ring timeouts specified for a specific trunk
03957        * on the station, then use the highest per-trunk ring timeout.
03958        * Otherwise, use the ring timeout set for the entire station. */
03959       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
03960          struct sla_station_ref *station_ref;
03961          int trunk_time_elapsed, trunk_time_left;
03962 
03963          AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
03964             if (ringing_trunk->trunk == trunk_ref->trunk)
03965                break;
03966          }
03967          if (!ringing_trunk)
03968             continue;
03969 
03970          /* If there is a trunk that is ringing without a timeout, then the
03971           * only timeout that could matter is a global station ring timeout. */
03972          if (!trunk_ref->ring_timeout)
03973             break;
03974 
03975          /* This trunk on this station is ringing and has a timeout.
03976           * However, make sure this trunk isn't still ringing from a
03977           * previous timeout.  If so, don't consider it. */
03978          AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
03979             if (station_ref->station == ringing_station->station)
03980                break;
03981          }
03982          if (station_ref)
03983             continue;
03984 
03985          trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
03986          trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
03987          if (trunk_time_left > final_trunk_time_left)
03988             final_trunk_time_left = trunk_time_left;
03989       }
03990 
03991       /* No timeout was found for ringing trunks, and no timeout for the entire station */
03992       if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
03993          continue;
03994 
03995       /* Compute how much time is left for a global station timeout */
03996       if (ringing_station->station->ring_timeout) {
03997          ring_timeout = ringing_station->station->ring_timeout;
03998          time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
03999          time_left = (ring_timeout * 1000) - time_elapsed;
04000       }
04001 
04002       /* If the time left based on the per-trunk timeouts is smaller than the
04003        * global station ring timeout, use that. */
04004       if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
04005          time_left = final_trunk_time_left;
04006 
04007       /* If there is no time left, the station needs to stop ringing */
04008       if (time_left <= 0) {
04009          AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
04010          sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
04011          res = 1;
04012          continue;
04013       }
04014 
04015       /* There is still some time left for this station to ring, so save that
04016        * timeout if it is the first event scheduled to occur */
04017       if (time_left < *timeout)
04018          *timeout = time_left;
04019    }
04020    AST_LIST_TRAVERSE_SAFE_END
04021 
04022    return res;
04023 }
04024 
04025 /*! \brief Calculate the ring delay for a station
04026  * \note Assumes sla.lock is locked
04027  */
04028 static int sla_calc_station_delays(unsigned int *timeout)
04029 {
04030    struct sla_station *station;
04031    int res = 0;
04032 
04033    AST_LIST_TRAVERSE(&sla_stations, station, entry) {
04034       struct sla_ringing_trunk *ringing_trunk;
04035       int time_left;
04036 
04037       /* Ignore stations already ringing */
04038       if (sla_check_ringing_station(station))
04039          continue;
04040 
04041       /* Ignore stations already on a call */
04042       if (sla_check_inuse_station(station))
04043          continue;
04044 
04045       /* Ignore stations that don't have one of their trunks ringing */
04046       if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
04047          continue;
04048 
04049       if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
04050          continue;
04051 
04052       /* If there is no time left, then the station needs to start ringing.
04053        * Return non-zero so that an event will be queued up an event to 
04054        * make that happen. */
04055       if (time_left <= 0) {
04056          res = 1;
04057          continue;
04058       }
04059 
04060       if (time_left < *timeout)
04061          *timeout = time_left;
04062    }
04063 
04064    return res;
04065 }
04066 
04067 /*! \brief Calculate the time until the next known event
04068  *  \note Called with sla.lock locked */
04069 static int sla_process_timers(struct timespec *ts)
04070 {
04071    unsigned int timeout = UINT_MAX;
04072    struct timeval tv;
04073    unsigned int change_made = 0;
04074 
04075    /* Check for ring timeouts on ringing trunks */
04076    if (sla_calc_trunk_timeouts(&timeout))
04077       change_made = 1;
04078 
04079    /* Check for ring timeouts on ringing stations */
04080    if (sla_calc_station_timeouts(&timeout))
04081       change_made = 1;
04082 
04083    /* Check for station ring delays */
04084    if (sla_calc_station_delays(&timeout))
04085       change_made = 1;
04086 
04087    /* queue reprocessing of ringing trunks */
04088    if (change_made)
04089       sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
04090 
04091    /* No timeout */
04092    if (timeout == UINT_MAX)
04093       return 0;
04094 
04095    if (ts) {
04096       tv = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
04097       ts->tv_sec = tv.tv_sec;
04098       ts->tv_nsec = tv.tv_usec * 1000;
04099    }
04100 
04101    return 1;
04102 }
04103 
04104 static void *sla_thread(void *data)
04105 {
04106    struct sla_failed_station *failed_station;
04107    struct sla_ringing_station *ringing_station;
04108 
04109    ast_mutex_lock(&sla.lock);
04110 
04111    while (!sla.stop) {
04112       struct sla_event *event;
04113       struct timespec ts = { 0, };
04114       unsigned int have_timeout = 0;
04115 
04116       if (AST_LIST_EMPTY(&sla.event_q)) {
04117          if ((have_timeout = sla_process_timers(&ts)))
04118             ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
04119          else
04120             ast_cond_wait(&sla.cond, &sla.lock);
04121          if (sla.stop)
04122             break;
04123       }
04124 
04125       if (have_timeout)
04126          sla_process_timers(NULL);
04127 
04128       while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
04129          ast_mutex_unlock(&sla.lock);
04130          switch (event->type) {
04131          case SLA_EVENT_HOLD:
04132             sla_handle_hold_event(event);
04133             break;
04134          case SLA_EVENT_DIAL_STATE:
04135             sla_handle_dial_state_event();
04136             break;
04137          case SLA_EVENT_RINGING_TRUNK:
04138             sla_handle_ringing_trunk_event();
04139             break;
04140          }
04141          free(event);
04142          ast_mutex_lock(&sla.lock);
04143       }
04144    }
04145 
04146    ast_mutex_unlock(&sla.lock);
04147 
04148    while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry)))
04149       free(ringing_station);
04150 
04151    while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry)))
04152       free(failed_station);
04153 
04154    return NULL;
04155 }
04156 
04157 struct dial_trunk_args {
04158    struct sla_trunk_ref *trunk_ref;
04159    struct sla_station *station;
04160    ast_mutex_t *cond_lock;
04161    ast_cond_t *cond;
04162 };
04163 
04164 static void *dial_trunk(void *data)
04165 {
04166    struct dial_trunk_args *args = data;
04167    struct ast_dial *dial;
04168    char *tech, *tech_data;
04169    enum ast_dial_result dial_res;
04170    char conf_name[MAX_CONFNUM];
04171    struct ast_conference *conf;
04172    struct ast_flags conf_flags = { 0 };
04173    struct sla_trunk_ref *trunk_ref = args->trunk_ref;
04174    const char *cid_name = NULL, *cid_num = NULL;
04175 
04176    if (!(dial = ast_dial_create())) {
04177       ast_mutex_lock(args->cond_lock);
04178       ast_cond_signal(args->cond);
04179       ast_mutex_unlock(args->cond_lock);
04180       return NULL;
04181    }
04182 
04183    tech_data = ast_strdupa(trunk_ref->trunk->device);
04184    tech = strsep(&tech_data, "/");
04185    if (ast_dial_append(dial, tech, tech_data) == -1) {
04186       ast_mutex_lock(args->cond_lock);
04187       ast_cond_signal(args->cond);
04188       ast_mutex_unlock(args->cond_lock);
04189       ast_dial_destroy(dial);
04190       return NULL;
04191    }
04192 
04193    if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_name)) {
04194       cid_name = ast_strdupa(trunk_ref->chan->cid.cid_name);
04195       free(trunk_ref->chan->cid.cid_name);
04196       trunk_ref->chan->cid.cid_name = NULL;
04197    }
04198    if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_num)) {
04199       cid_num = ast_strdupa(trunk_ref->chan->cid.cid_num);
04200       free(trunk_ref->chan->cid.cid_num);
04201       trunk_ref->chan->cid.cid_num = NULL;
04202    }
04203 
04204    dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
04205 
04206    if (cid_name)
04207       trunk_ref->chan->cid.cid_name = ast_strdup(cid_name);
04208    if (cid_num)
04209       trunk_ref->chan->cid.cid_num = ast_strdup(cid_num);
04210 
04211    if (dial_res != AST_DIAL_RESULT_TRYING) {
04212       ast_mutex_lock(args->cond_lock);
04213       ast_cond_signal(args->cond);
04214       ast_mutex_unlock(args->cond_lock);
04215       ast_dial_destroy(dial);
04216       return NULL;
04217    }
04218 
04219    for (;;) {
04220       unsigned int done = 0;
04221       switch ((dial_res = ast_dial_state(dial))) {
04222       case AST_DIAL_RESULT_ANSWERED:
04223          trunk_ref->trunk->chan = ast_dial_answered(dial);
04224       case AST_DIAL_RESULT_HANGUP:
04225       case AST_DIAL_RESULT_INVALID:
04226       case AST_DIAL_RESULT_FAILED:
04227       case AST_DIAL_RESULT_TIMEOUT:
04228       case AST_DIAL_RESULT_UNANSWERED:
04229          done = 1;
04230       case AST_DIAL_RESULT_TRYING:
04231       case AST_DIAL_RESULT_RINGING:
04232       case AST_DIAL_RESULT_PROGRESS:
04233       case AST_DIAL_RESULT_PROCEEDING:
04234          break;
04235       }
04236       if (done)
04237          break;
04238    }
04239 
04240    if (!trunk_ref->trunk->chan) {
04241       ast_mutex_lock(args->cond_lock);
04242       ast_cond_signal(args->cond);
04243       ast_mutex_unlock(args->cond_lock);
04244       ast_dial_join(dial);
04245       ast_dial_destroy(dial);
04246       return NULL;
04247    }
04248 
04249    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
04250    ast_set_flag(&conf_flags, 
04251       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | 
04252       CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
04253    conf = build_conf(conf_name, "", "", 1, 1, 1);
04254 
04255    ast_mutex_lock(args->cond_lock);
04256    ast_cond_signal(args->cond);
04257    ast_mutex_unlock(args->cond_lock);
04258 
04259    if (conf) {
04260       conf_run(trunk_ref->trunk->chan, conf, conf_flags.flags, NULL);
04261       dispose_conf(conf);
04262       conf = NULL;
04263    }
04264 
04265    /* If the trunk is going away, it is definitely now IDLE. */
04266    sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
04267 
04268    trunk_ref->trunk->chan = NULL;
04269    trunk_ref->trunk->on_hold = 0;
04270 
04271    ast_dial_join(dial);
04272    ast_dial_destroy(dial);
04273 
04274    return NULL;
04275 }
04276 
04277 /*! \brief For a given station, choose the highest priority idle trunk
04278  */
04279 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
04280 {
04281    struct sla_trunk_ref *trunk_ref = NULL;
04282 
04283    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
04284       if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
04285          break;
04286    }
04287 
04288    return trunk_ref;
04289 }
04290 
04291 static int sla_station_exec(struct ast_channel *chan, void *data)
04292 {
04293    char *station_name, *trunk_name;
04294    struct sla_station *station;
04295    struct sla_trunk_ref *trunk_ref = NULL;
04296    char conf_name[MAX_CONFNUM];
04297    struct ast_flags conf_flags = { 0 };
04298    struct ast_conference *conf;
04299 
04300    if (ast_strlen_zero(data)) {
04301       ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
04302       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
04303       return 0;
04304    }
04305 
04306    trunk_name = ast_strdupa(data);
04307    station_name = strsep(&trunk_name, "_");
04308 
04309    if (ast_strlen_zero(station_name)) {
04310       ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
04311       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
04312       return 0;
04313    }
04314 
04315    AST_RWLIST_RDLOCK(&sla_stations);
04316    station = sla_find_station(station_name);
04317    AST_RWLIST_UNLOCK(&sla_stations);
04318 
04319    if (!station) {
04320       ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
04321       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
04322       return 0;
04323    }
04324 
04325    AST_RWLIST_RDLOCK(&sla_trunks);
04326    if (!ast_strlen_zero(trunk_name)) {
04327       trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
04328    } else
04329       trunk_ref = sla_choose_idle_trunk(station);
04330    AST_RWLIST_UNLOCK(&sla_trunks);
04331 
04332    if (!trunk_ref) {
04333       if (ast_strlen_zero(trunk_name))
04334          ast_log(LOG_NOTICE, "No trunks available for call.\n");
04335       else {
04336          ast_log(LOG_NOTICE, "Can't join existing call on trunk "
04337             "'%s' due to access controls.\n", trunk_name);
04338       }
04339       pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
04340       return 0;
04341    }
04342 
04343    if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
04344       if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
04345          sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
04346       else {
04347          trunk_ref->state = SLA_TRUNK_STATE_UP;
04348          ast_device_state_changed("SLA:%s_%s", station->name, trunk_ref->trunk->name);
04349       }
04350    } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
04351       struct sla_ringing_trunk *ringing_trunk;
04352 
04353       ast_mutex_lock(&sla.lock);
04354       AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
04355          if (ringing_trunk->trunk == trunk_ref->trunk) {
04356             AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
04357             break;
04358          }
04359       }
04360       AST_LIST_TRAVERSE_SAFE_END
04361       ast_mutex_unlock(&sla.lock);
04362 
04363       if (ringing_trunk) {
04364          answer_trunk_chan(ringing_trunk->trunk->chan);
04365          sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
04366 
04367          free(ringing_trunk);
04368 
04369          /* Queue up reprocessing ringing trunks, and then ringing stations again */
04370          sla_queue_event(SLA_EVENT_RINGING_TRUNK);
04371          sla_queue_event(SLA_EVENT_DIAL_STATE);
04372       }
04373    }
04374 
04375    trunk_ref->chan = chan;
04376 
04377    if (!trunk_ref->trunk->chan) {
04378       ast_mutex_t cond_lock;
04379       ast_cond_t cond;
04380       pthread_t dont_care;
04381       pthread_attr_t attr;
04382       struct dial_trunk_args args = {
04383          .trunk_ref = trunk_ref,
04384          .station = station,
04385          .cond_lock = &cond_lock,
04386          .cond = &cond,
04387       };
04388       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
04389       /* Create a thread to dial the trunk and dump it into the conference.
04390        * However, we want to wait until the trunk has been dialed and the
04391        * conference is created before continuing on here. */
04392       ast_autoservice_start(chan);
04393       ast_mutex_init(&cond_lock);
04394       ast_cond_init(&cond, NULL);
04395       pthread_attr_init(&attr);
04396       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
04397       ast_mutex_lock(&cond_lock);
04398       ast_pthread_create_background(&dont_care, &attr, dial_trunk, &args);
04399       ast_cond_wait(&cond, &cond_lock);
04400       ast_mutex_unlock(&cond_lock);
04401       ast_mutex_destroy(&cond_lock);
04402       ast_cond_destroy(&cond);
04403       pthread_attr_destroy(&attr);
04404       ast_autoservice_stop(chan);
04405       if (!trunk_ref->trunk->chan) {
04406          ast_log(LOG_DEBUG, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
04407          pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
04408          sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
04409          trunk_ref->chan = NULL;
04410          return 0;
04411       }
04412    }
04413 
04414    if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
04415       trunk_ref->trunk->on_hold) {
04416       trunk_ref->trunk->on_hold = 0;
04417       ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
04418       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
04419    }
04420 
04421    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
04422    ast_set_flag(&conf_flags, 
04423       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
04424    ast_answer(chan);
04425    conf = build_conf(conf_name, "", "", 0, 0, 1);
04426    if (conf) {
04427       conf_run(chan, conf, conf_flags.flags, NULL);
04428       dispose_conf(conf);
04429       conf = NULL;
04430    }
04431    trunk_ref->chan = NULL;
04432    if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
04433       trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
04434       strncat(conf_name, "|K", sizeof(conf_name) - strlen(conf_name) - 1);
04435       admin_exec(NULL, conf_name);
04436       trunk_ref->trunk->hold_stations = 0;
04437       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
04438    }
04439    
04440    pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
04441 
04442    return 0;
04443 }
04444 
04445 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
04446 {
04447    struct sla_trunk_ref *trunk_ref;
04448 
04449    if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
04450       return NULL;
04451 
04452    trunk_ref->trunk = trunk;
04453 
04454    return trunk_ref;
04455 }
04456 
04457 static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
04458 {
04459    struct sla_ringing_trunk *ringing_trunk;
04460 
04461    if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
04462       return NULL;
04463    
04464    ringing_trunk->trunk = trunk;
04465    ringing_trunk->ring_begin = ast_tvnow();
04466 
04467    sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
04468 
04469    ast_mutex_lock(&sla.lock);
04470    AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
04471    ast_mutex_unlock(&sla.lock);
04472 
04473    sla_queue_event(SLA_EVENT_RINGING_TRUNK);
04474 
04475    return ringing_trunk;
04476 }
04477 
04478 static int sla_trunk_exec(struct ast_channel *chan, void *data)
04479 {
04480    const char *trunk_name = data;
04481    char conf_name[MAX_CONFNUM];
04482    struct ast_conference *conf;
04483    struct ast_flags conf_flags = { 0 };
04484    struct sla_trunk *trunk;
04485    struct sla_ringing_trunk *ringing_trunk;
04486 
04487    AST_RWLIST_RDLOCK(&sla_trunks);
04488    trunk = sla_find_trunk(trunk_name);
04489    AST_RWLIST_UNLOCK(&sla_trunks);
04490    if (!trunk) {
04491       ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", trunk_name);
04492       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
04493       return 0;
04494    }
04495    if (trunk->chan) {
04496       ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
04497          trunk_name);
04498       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
04499       return 0;
04500    }
04501    trunk->chan = chan;
04502 
04503    if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
04504       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
04505       return 0;
04506    }
04507 
04508    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_name);
04509    conf = build_conf(conf_name, "", "", 1, 1, 1);
04510    if (!conf) {
04511       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
04512       return 0;
04513    }
04514    ast_set_flag(&conf_flags, 
04515       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF | CONFFLAG_NO_AUDIO_UNTIL_UP);
04516    ast_indicate(chan, AST_CONTROL_RINGING);
04517    conf_run(chan, conf, conf_flags.flags, NULL);
04518    dispose_conf(conf);
04519    conf = NULL;
04520    trunk->chan = NULL;
04521    trunk->on_hold = 0;
04522 
04523    sla_change_trunk_state(trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
04524 
04525    if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
04526       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
04527 
04528    /* Remove the entry from the list of ringing trunks if it is still there. */
04529    ast_mutex_lock(&sla.lock);
04530    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
04531       if (ringing_trunk->trunk == trunk) {
04532          AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
04533          break;
04534       }
04535    }
04536    AST_LIST_TRAVERSE_SAFE_END
04537    ast_mutex_unlock(&sla.lock);
04538    if (ringing_trunk) {
04539       free(ringing_trunk);
04540       pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
04541       /* Queue reprocessing of ringing trunks to make stations stop ringing
04542        * that shouldn't be ringing after this trunk stopped. */
04543       sla_queue_event(SLA_EVENT_RINGING_TRUNK);
04544    }
04545 
04546    return 0;
04547 }
04548 
04549 static int sla_state(const char *data)
04550 {
04551    char *buf, *station_name, *trunk_name;
04552    struct sla_station *station;
04553    struct sla_trunk_ref *trunk_ref;
04554    int res = AST_DEVICE_INVALID;
04555 
04556    trunk_name = buf = ast_strdupa(data);
04557    station_name = strsep(&trunk_name, "_");
04558 
04559    AST_RWLIST_RDLOCK(&sla_stations);
04560    AST_LIST_TRAVERSE(&sla_stations, station, entry) {
04561       if (strcasecmp(station_name, station->name))
04562          continue;
04563       AST_RWLIST_RDLOCK(&sla_trunks);
04564       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
04565          if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
04566             break;
04567       }
04568       if (!trunk_ref) {
04569          AST_RWLIST_UNLOCK(&sla_trunks);
04570          break;
04571       }
04572       switch (trunk_ref->state) {
04573       case SLA_TRUNK_STATE_IDLE:
04574          res = AST_DEVICE_NOT_INUSE;
04575          break;
04576       case SLA_TRUNK_STATE_RINGING:
04577          res = AST_DEVICE_RINGING;
04578          break;
04579       case SLA_TRUNK_STATE_UP:
04580          res = AST_DEVICE_INUSE;
04581          break;
04582       case SLA_TRUNK_STATE_ONHOLD:
04583       case SLA_TRUNK_STATE_ONHOLD_BYME:
04584          res = AST_DEVICE_ONHOLD;
04585          break;
04586       }
04587       AST_RWLIST_UNLOCK(&sla_trunks);
04588    }
04589    AST_RWLIST_UNLOCK(&sla_stations);
04590 
04591    if (res == AST_DEVICE_INVALID) {
04592       ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
04593          trunk_name, station_name);
04594    }
04595 
04596    return res;
04597 }
04598 
04599 static void destroy_trunk(struct sla_trunk *trunk)
04600 {
04601    struct sla_station_ref *station_ref;
04602 
04603    if (!ast_strlen_zero(trunk->autocontext))
04604       ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
04605 
04606    while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
04607       free(station_ref);
04608 
04609    ast_string_field_free_memory(trunk);
04610    free(trunk);
04611 }
04612 
04613 static void destroy_station(struct sla_station *station)
04614 {
04615    struct sla_trunk_ref *trunk_ref;
04616 
04617    if (!ast_strlen_zero(station->autocontext)) {
04618       AST_RWLIST_RDLOCK(&sla_trunks);
04619       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
04620          char exten[AST_MAX_EXTENSION];
04621          char hint[AST_MAX_APP];
04622          snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
04623          snprintf(hint, sizeof(hint), "SLA:%s", exten);
04624          ast_context_remove_extension(station->autocontext, exten, 
04625             1, sla_registrar);
04626          ast_context_remove_extension(station->autocontext, hint, 
04627             PRIORITY_HINT, sla_registrar);
04628       }
04629       AST_RWLIST_UNLOCK(&sla_trunks);
04630    }
04631 
04632    while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
04633       free(trunk_ref);
04634 
04635    ast_string_field_free_memory(station);
04636    free(station);
04637 }
04638 
04639 static void sla_destroy(void)
04640 {
04641    struct sla_trunk *trunk;
04642    struct sla_station *station;
04643 
04644    AST_RWLIST_WRLOCK(&sla_trunks);
04645    while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
04646       destroy_trunk(trunk);
04647    AST_RWLIST_UNLOCK(&sla_trunks);
04648 
04649    AST_RWLIST_WRLOCK(&sla_stations);
04650    while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
04651       destroy_station(station);
04652    AST_RWLIST_UNLOCK(&sla_stations);
04653 
04654    if (sla.thread != AST_PTHREADT_NULL) {
04655       ast_mutex_lock(&sla.lock);
04656       sla.stop = 1;
04657       ast_cond_signal(&sla.cond);
04658       ast_mutex_unlock(&sla.lock);
04659       pthread_join(sla.thread, NULL);
04660    }
04661 
04662    /* Drop any created contexts from the dialplan */
04663    ast_context_destroy(NULL, sla_registrar);
04664 
04665    ast_mutex_destroy(&sla.lock);
04666    ast_cond_destroy(&sla.cond);
04667 }
04668 
04669 static int sla_check_device(const char *device)
04670 {
04671    char *tech, *tech_data;
04672 
04673    tech_data = ast_strdupa(device);
04674    tech = strsep(&tech_data, "/");
04675 
04676    if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
04677       return -1;
04678 
04679    return 0;
04680 }
04681 
04682 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
04683 {
04684    struct sla_trunk *trunk;
04685    struct ast_variable *var;
04686    const char *dev;
04687 
04688    if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
04689       ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
04690       return -1;
04691    }
04692 
04693    if (sla_check_device(dev)) {
04694       ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
04695          cat, dev);
04696       return -1;
04697    }
04698 
04699    if (!(trunk = ast_calloc(1, sizeof(*trunk))))
04700       return -1;
04701    if (ast_string_field_init(trunk, 32)) {
04702       free(trunk);
04703       return -1;
04704    }
04705 
04706    ast_string_field_set(trunk, name, cat);
04707    ast_string_field_set(trunk, device, dev);
04708 
04709    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
04710       if (!strcasecmp(var->name, "autocontext"))
04711          ast_string_field_set(trunk, autocontext, var->value);
04712       else if (!strcasecmp(var->name, "ringtimeout")) {
04713          if (sscanf(var->value, "%u", &trunk->ring_timeout) != 1) {
04714             ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
04715                var->value, trunk->name);
04716             trunk->ring_timeout = 0;
04717          }
04718       } else if (!strcasecmp(var->name, "barge"))
04719          trunk->barge_disabled = ast_false(var->value);
04720       else if (!strcasecmp(var->name, "hold")) {
04721          if (!strcasecmp(var->value, "private"))
04722             trunk->hold_access = SLA_HOLD_PRIVATE;
04723          else if (!strcasecmp(var->value, "open"))
04724             trunk->hold_access = SLA_HOLD_OPEN;
04725          else {
04726             ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
04727                var->value, trunk->name);
04728          }
04729       } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
04730          ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
04731             var->name, var->lineno, SLA_CONFIG_FILE);
04732       }
04733    }
04734 
04735    if (!ast_strlen_zero(trunk->autocontext)) {
04736       struct ast_context *context;
04737       context = ast_context_find_or_create(NULL, trunk->autocontext, sla_registrar);
04738       if (!context) {
04739          ast_log(LOG_ERROR, "Failed to automatically find or create "
04740             "context '%s' for SLA!\n", trunk->autocontext);
04741          destroy_trunk(trunk);
04742          return -1;
04743       }
04744       if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
04745          NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free, sla_registrar)) {
04746          ast_log(LOG_ERROR, "Failed to automatically create extension "
04747             "for trunk '%s'!\n", trunk->name);
04748          destroy_trunk(trunk);
04749          return -1;
04750       }
04751    }
04752 
04753    AST_RWLIST_WRLOCK(&sla_trunks);
04754    AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
04755    AST_RWLIST_UNLOCK(&sla_trunks);
04756 
04757    return 0;
04758 }
04759 
04760 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
04761 {
04762    struct sla_trunk *trunk;
04763    struct sla_trunk_ref *trunk_ref;
04764    struct sla_station_ref *station_ref;
04765    char *trunk_name, *options, *cur;
04766 
04767    options = ast_strdupa(var->value);
04768    trunk_name = strsep(&options, ",");
04769    
04770    AST_RWLIST_RDLOCK(&sla_trunks);
04771    AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
04772       if (!strcasecmp(trunk->name, trunk_name))
04773          break;
04774    }
04775 
04776    AST_RWLIST_UNLOCK(&sla_trunks);
04777    if (!trunk) {
04778       ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
04779       return;
04780    }
04781    if (!(trunk_ref = create_trunk_ref(trunk)))
04782       return;
04783    trunk_ref->state = SLA_TRUNK_STATE_IDLE;
04784 
04785    while ((cur = strsep(&options, ","))) {
04786       char *name, *value = cur;
04787       name = strsep(&value, "=");
04788       if (!strcasecmp(name, "ringtimeout")) {
04789          if (sscanf(value, "%u", &trunk_ref->ring_timeout) != 1) {
04790             ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
04791                "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
04792             trunk_ref->ring_timeout = 0;
04793          }
04794       } else if (!strcasecmp(name, "ringdelay")) {
04795          if (sscanf(value, "%u", &trunk_ref->ring_delay) != 1) {
04796             ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
04797                "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
04798             trunk_ref->ring_delay = 0;
04799          }
04800       } else {
04801          ast_log(LOG_WARNING, "Invalid option '%s' for "
04802             "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
04803       }
04804    }
04805 
04806    if (!(station_ref = sla_create_station_ref(station))) {
04807       free(trunk_ref);
04808       return;
04809    }
04810    ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
04811    AST_RWLIST_WRLOCK(&sla_trunks);
04812    AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
04813    AST_RWLIST_UNLOCK(&sla_trunks);
04814    AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
04815 }
04816 
04817 static int sla_build_station(struct ast_config *cfg, const char *cat)
04818 {
04819    struct sla_station *station;
04820    struct ast_variable *var;
04821    const char *dev;
04822 
04823    if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
04824       ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
04825       return -1;
04826    }
04827 
04828    if (!(station = ast_calloc(1, sizeof(*station))))
04829       return -1;
04830    if (ast_string_field_init(station, 32)) {
04831       free(station);
04832       return -1;
04833    }
04834 
04835    ast_string_field_set(station, name, cat);
04836    ast_string_field_set(station, device, dev);
04837 
04838    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
04839       if (!strcasecmp(var->name, "trunk"))
04840          sla_add_trunk_to_station(station, var);
04841       else if (!strcasecmp(var->name, "autocontext"))
04842          ast_string_field_set(station, autocontext, var->value);
04843       else if (!strcasecmp(var->name, "ringtimeout")) {
04844          if (sscanf(var->value, "%u", &station->ring_timeout) != 1) {
04845             ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
04846                var->value, station->name);
04847             station->ring_timeout = 0;
04848          }
04849       } else if (!strcasecmp(var->name, "ringdelay")) {
04850          if (sscanf(var->value, "%u", &station->ring_delay) != 1) {
04851             ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
04852                var->value, station->name);
04853             station->ring_delay = 0;
04854          }
04855       } else if (!strcasecmp(var->name, "hold")) {
04856          if (!strcasecmp(var->value, "private"))
04857             station->hold_access = SLA_HOLD_PRIVATE;
04858          else if (!strcasecmp(var->value, "open"))
04859             station->hold_access = SLA_HOLD_OPEN;
04860          else {
04861             ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
04862                var->value, station->name);
04863          }
04864 
04865       } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
04866          ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
04867             var->name, var->lineno, SLA_CONFIG_FILE);
04868       }
04869    }
04870 
04871    if (!ast_strlen_zero(station->autocontext)) {
04872       struct ast_context *context;
04873       struct sla_trunk_ref *trunk_ref;
04874       context = ast_context_find_or_create(NULL, station->autocontext, sla_registrar);
04875       if (!context) {
04876          ast_log(LOG_ERROR, "Failed to automatically find or create "
04877             "context '%s' for SLA!\n", station->autocontext);
04878          destroy_station(station);
04879          return -1;
04880       }
04881       /* The extension for when the handset goes off-hook.
04882        * exten => station1,1,SLAStation(station1) */
04883       if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
04884          NULL, NULL, slastation_app, ast_strdup(station->name), ast_free, sla_registrar)) {
04885          ast_log(LOG_ERROR, "Failed to automatically create extension "
04886             "for trunk '%s'!\n", station->name);
04887          destroy_station(station);
04888          return -1;
04889       }
04890       AST_RWLIST_RDLOCK(&sla_trunks);
04891       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
04892          char exten[AST_MAX_EXTENSION];
04893          char hint[AST_MAX_APP];
04894          snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
04895          snprintf(hint, sizeof(hint), "SLA:%s", exten);
04896          /* Extension for this line button 
04897           * exten => station1_line1,1,SLAStation(station1_line1) */
04898          if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
04899             NULL, NULL, slastation_app, ast_strdup(exten), ast_free, sla_registrar)) {
04900             ast_log(LOG_ERROR, "Failed to automatically create extension "
04901                "for trunk '%s'!\n", station->name);
04902             destroy_station(station);
04903             return -1;
04904          }
04905          /* Hint for this line button 
04906           * exten => station1_line1,hint,SLA:station1_line1 */
04907          if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
04908             NULL, NULL, hint, NULL, NULL, sla_registrar)) {
04909             ast_log(LOG_ERROR, "Failed to automatically create hint "
04910                "for trunk '%s'!\n", station->name);
04911             destroy_station(station);
04912             return -1;
04913          }
04914       }
04915       AST_RWLIST_UNLOCK(&sla_trunks);
04916    }
04917 
04918    AST_RWLIST_WRLOCK(&sla_stations);
04919    AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
04920    AST_RWLIST_UNLOCK(&sla_stations);
04921 
04922    return 0;
04923 }
04924 
04925 static int sla_load_config(void)
04926 {
04927    struct ast_config *cfg;
04928    const char *cat = NULL;
04929    int res = 0;
04930    const char *val;
04931 
04932    ast_mutex_init(&sla.lock);
04933    ast_cond_init(&sla.cond, NULL);
04934 
04935    if (!(cfg = ast_config_load(SLA_CONFIG_FILE)))
04936       return 0; /* Treat no config as normal */
04937 
04938    if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
04939       sla.attempt_callerid = ast_true(val);
04940 
04941    while ((cat = ast_category_browse(cfg, cat)) && !res) {
04942       const char *type;
04943       if (!strcasecmp(cat, "general"))
04944          continue;
04945       if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
04946          ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
04947             SLA_CONFIG_FILE);
04948          continue;
04949       }
04950       if (!strcasecmp(type, "trunk"))
04951          res = sla_build_trunk(cfg, cat);
04952       else if (!strcasecmp(type, "station"))
04953          res = sla_build_station(cfg, cat);
04954       else {
04955          ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
04956             SLA_CONFIG_FILE, type);
04957       }
04958    }
04959 
04960    ast_config_destroy(cfg);
04961 
04962    if (!AST_LIST_EMPTY(&sla_stations) || !AST_LIST_EMPTY(&sla_stations))
04963       ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
04964 
04965    return res;
04966 }
04967 
04968 static int load_config(int reload)
04969 {
04970    int res = 0;
04971 
04972    load_config_meetme();
04973    if (!reload)
04974       res = sla_load_config();
04975 
04976    return res;
04977 }
04978 
04979 static int unload_module(void)
04980 {
04981    int res = 0;
04982    
04983    ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
04984    res = ast_manager_unregister("MeetmeMute");
04985    res |= ast_manager_unregister("MeetmeUnmute");
04986    res |= ast_unregister_application(app3);
04987    res |= ast_unregister_application(app2);
04988    res |= ast_unregister_application(app);
04989    res |= ast_unregister_application(slastation_app);
04990    res |= ast_unregister_application(slatrunk_app);
04991 
04992    ast_devstate_prov_del("Meetme");
04993    ast_devstate_prov_del("SLA");
04994 
04995    ast_module_user_hangup_all();
04996    
04997    sla_destroy();
04998 
04999    return res;
05000 }
05001 
05002 static int load_module(void)
05003 {
05004    int res = 0;
05005 
05006    res |= load_config(0);
05007 
05008    ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
05009    res |= ast_manager_register("MeetmeMute", EVENT_FLAG_CALL, 
05010                 action_meetmemute, "Mute a Meetme user");
05011    res |= ast_manager_register("MeetmeUnmute", EVENT_FLAG_CALL, 
05012                 action_meetmeunmute, "Unmute a Meetme user");
05013    res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
05014    res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
05015    res |= ast_register_application(app, conf_exec, synopsis, descrip);
05016    res |= ast_register_application(slastation_app, sla_station_exec,
05017                slastation_synopsis, slastation_desc);
05018    res |= ast_register_application(slatrunk_app, sla_trunk_exec,
05019                slatrunk_synopsis, slatrunk_desc);
05020 
05021    res |= ast_devstate_prov_add("Meetme", meetmestate);
05022    res |= ast_devstate_prov_add("SLA", sla_state);
05023 
05024    return res;
05025 }
05026 
05027 static int reload(void)
05028 {
05029    return load_config(1);
05030 }
05031 
05032 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MeetMe conference bridge",
05033       .load = load_module,
05034       .unload = unload_module,
05035       .reload = reload,
05036           );
05037 

Generated on Tue Apr 28 22:49:57 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7