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

Generated on Thu Dec 17 17:39:41 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7