Thu Oct 8 21:57:17 2009

Asterisk developer's documentation


app_meetme.c

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

Generated on Thu Oct 8 21:57:18 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.8