Fri Aug 24 02:22:09 2007

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

Generated on Fri Aug 24 02:22:10 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1