Sat Nov 1 06:28:19 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