Mon Nov 24 15:33:57 2008

Asterisk developer's documentation


app_meetme.c

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

Generated on Mon Nov 24 15:33:59 2008 for Asterisk - the Open Source PBX by  doxygen 1.4.7