Fix option handling code.
[asterisk/asterisk.git] / apps / app_meetme.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2007, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * SLA Implementation by:
9  * Russell Bryant <russell@digium.com>
10  *
11  * See http://www.asterisk.org for more information about
12  * the Asterisk project. Please do not directly contact
13  * any of the maintainers of this project for assistance;
14  * the project provides a web site, mailing lists and IRC
15  * channels for your use.
16  *
17  * This program is free software, distributed under the terms of
18  * the GNU General Public License Version 2. See the LICENSE file
19  * at the top of the source tree.
20  */
21
22 /*! \file
23  *
24  * \brief Meet me conference bridge and Shared Line Appearances
25  *
26  * \author Mark Spencer <markster@digium.com>
27  * \author (SLA) Russell Bryant <russell@digium.com>
28  * 
29  * \ingroup applications
30  */
31
32 /*** MODULEINFO
33         <depend>dahdi</depend>
34  ***/
35
36 #include "asterisk.h"
37
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
39
40 #include <dahdi/user.h>
41
42 #include "asterisk/lock.h"
43 #include "asterisk/file.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/module.h"
47 #include "asterisk/config.h"
48 #include "asterisk/app.h"
49 #include "asterisk/dsp.h"
50 #include "asterisk/musiconhold.h"
51 #include "asterisk/manager.h"
52 #include "asterisk/cli.h"
53 #include "asterisk/say.h"
54 #include "asterisk/utils.h"
55 #include "asterisk/translate.h"
56 #include "asterisk/ulaw.h"
57 #include "asterisk/devicestate.h"
58 #include "asterisk/dial.h"
59 #include "asterisk/causes.h"
60 #include "asterisk/paths.h"
61
62 #include "enter.h"
63 #include "leave.h"
64
65 #define CONFIG_FILE_NAME "meetme.conf"
66 #define SLA_CONFIG_FILE  "sla.conf"
67
68 /*! each buffer is 20ms, so this is 640ms total */
69 #define DEFAULT_AUDIO_BUFFERS  32
70
71 /*! String format for scheduled conferences */
72 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
73
74 enum {
75         ADMINFLAG_MUTED =     (1 << 1), /*!< User is muted */
76         ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
77         ADMINFLAG_KICKME =    (1 << 3),  /*!< User has been kicked */
78         /*! User has requested to speak */
79         ADMINFLAG_T_REQUEST = (1 << 4),
80 };
81
82 #define MEETME_DELAYDETECTTALK     300
83 #define MEETME_DELAYDETECTENDTALK  1000
84
85 #define AST_FRAME_BITS  32
86
87 enum volume_action {
88         VOL_UP,
89         VOL_DOWN
90 };
91
92 enum entrance_sound {
93         ENTER,
94         LEAVE
95 };
96
97 enum recording_state {
98         MEETME_RECORD_OFF,
99         MEETME_RECORD_STARTED,
100         MEETME_RECORD_ACTIVE,
101         MEETME_RECORD_TERMINATE
102 };
103
104 #define CONF_SIZE  320
105
106 enum {
107         /*! user has admin access on the conference */
108         CONFFLAG_ADMIN = (1 << 0),
109         /*! If set the user can only receive audio from the conference */
110         CONFFLAG_MONITOR = (1 << 1),
111         /*! If set asterisk will exit conference when key defined in p() option is pressed */
112         CONFFLAG_KEYEXIT = (1 << 2),
113         /*! If set asterisk will provide a menu to the user when '*' is pressed */
114         CONFFLAG_STARMENU = (1 << 3),
115         /*! If set the use can only send audio to the conference */
116         CONFFLAG_TALKER = (1 << 4),
117         /*! If set there will be no enter or leave sounds */
118         CONFFLAG_QUIET = (1 << 5),
119         /*! If set, when user joins the conference, they will be told the number 
120          *  of users that are already in */
121         CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
122         /*! Set to run AGI Script in Background */
123         CONFFLAG_AGI = (1 << 7),
124         /*! Set to have music on hold when user is alone in conference */
125         CONFFLAG_MOH = (1 << 8),
126         /*! If set the MeetMe will return if all marked with this flag left */
127         CONFFLAG_MARKEDEXIT = (1 << 9),
128         /*! If set, the MeetMe will wait until a marked user enters */
129         CONFFLAG_WAITMARKED = (1 << 10),
130         /*! If set, the MeetMe will exit to the specified context */
131         CONFFLAG_EXIT_CONTEXT = (1 << 11),
132         /*! If set, the user will be marked */
133         CONFFLAG_MARKEDUSER = (1 << 12),
134         /*! If set, user will be ask record name on entry of conference */
135         CONFFLAG_INTROUSER = (1 << 13),
136         /*! If set, the MeetMe will be recorded */
137         CONFFLAG_RECORDCONF = (1<< 14),
138         /*! If set, the user will be monitored if the user is talking or not */
139         CONFFLAG_MONITORTALKER = (1 << 15),
140         CONFFLAG_DYNAMIC = (1 << 16),
141         CONFFLAG_DYNAMICPIN = (1 << 17),
142         CONFFLAG_EMPTY = (1 << 18),
143         CONFFLAG_EMPTYNOPIN = (1 << 19),
144         CONFFLAG_ALWAYSPROMPT = (1 << 20),
145         /*! If set, won't speak the extra prompt when the first person 
146          *  enters the conference */
147         CONFFLAG_NOONLYPERSON = (1 << 22),
148         /*! If set, user will be asked to record name on entry of conference 
149          *  without review */
150         CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
151         /*! If set, the user will be initially self-muted */
152         CONFFLAG_STARTMUTED = (1 << 24),
153         /*! Pass DTMF through the conference */
154         CONFFLAG_PASS_DTMF = (1 << 25),
155         CONFFLAG_SLA_STATION = (1 << 26),
156         CONFFLAG_SLA_TRUNK = (1 << 27),
157         /*! If set, the user should continue in the dialplan if kicked out */
158         CONFFLAG_KICK_CONTINUE = (1 << 28),
159         CONFFLAG_DURATION_STOP = (1 << 29),
160         CONFFLAG_DURATION_LIMIT = (1 << 30),
161 };
162
163 enum {
164         OPT_ARG_WAITMARKED = 0,
165         OPT_ARG_EXITKEYS   = 1,
166         OPT_ARG_DURATION_STOP = 2,
167         OPT_ARG_DURATION_LIMIT = 3,
168         OPT_ARG_MOH_CLASS = 4,
169         OPT_ARG_ARRAY_SIZE = 5,
170 };
171
172 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
173         AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
174         AST_APP_OPTION('a', CONFFLAG_ADMIN ),
175         AST_APP_OPTION('b', CONFFLAG_AGI ),
176         AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
177         AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
178         AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
179         AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
180         AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
181         AST_APP_OPTION('e', CONFFLAG_EMPTY ),
182         AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
183         AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
184         AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
185         AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
186         AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
187         AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
188         AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
189         AST_APP_OPTION('q', CONFFLAG_QUIET ),
190         AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
191         AST_APP_OPTION('s', CONFFLAG_STARMENU ),
192         AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
193         AST_APP_OPTION('l', CONFFLAG_MONITOR ),
194         AST_APP_OPTION('t', CONFFLAG_TALKER ),
195         AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
196         AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
197         AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
198         AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
199         AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
200         AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
201 END_OPTIONS );
202
203 static const char *app = "MeetMe";
204 static const char *app2 = "MeetMeCount";
205 static const char *app3 = "MeetMeAdmin";
206 static const char *app4 = "MeetMeChannelAdmin";
207 static const char *slastation_app = "SLAStation";
208 static const char *slatrunk_app = "SLATrunk";
209
210 static const char *synopsis = "MeetMe conference bridge";
211 static const char *synopsis2 = "MeetMe participant count";
212 static const char *synopsis3 = "MeetMe conference Administration";
213 static const char *synopsis4 = "MeetMe conference Administration (channel specific)";
214 static const char *slastation_synopsis = "Shared Line Appearance Station";
215 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
216
217 /* Lookup RealTime conferences based on confno and current time */
218 static int rt_schedule;
219 static int fuzzystart;
220 static int earlyalert;
221 static int endalert;
222 static int extendby;
223
224 /* Log participant count to the RealTime backend */
225 static int rt_log_members;
226
227 static const char *descrip =
228 "  MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
229 "conference.  If the conference number is omitted, the user will be prompted\n"
230 "to enter one.  User can exit the conference by hangup, or if the 'p' option\n"
231 "is specified, by pressing '#'.\n"
232 "Please note: The DAHDI kernel modules and at least one hardware driver (or dahdi_dummy)\n"
233 "             must be present for conferencing to operate properly. In addition, the chan_dahdi\n"
234 "             channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
235 "The option string may contain zero or more of the following characters:\n"
236 "      'a' -- set admin mode\n"
237 "      'A' -- set marked mode\n"
238 "      'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
239 "             Default: conf-background.agi  (Note: This does not work with\n"
240 "             non-DAHDI channels in the same conference)\n"
241 "      'c' -- announce user(s) count on joining a conference\n"
242 "      'C' -- continue in dialplan when kicked out of conference\n"
243 "      'd' -- dynamically add conference\n"
244 "      'D' -- dynamically add conference, prompting for a PIN\n"
245 "      'e' -- select an empty conference\n"
246 "      'E' -- select an empty pinless conference\n"
247 "      'F' -- Pass DTMF through the conference.\n"
248 "      'i' -- announce user join/leave with review\n"
249 "      'I' -- announce user join/leave without review\n"
250 "      'l' -- set listen only mode (Listen only, no talking)\n"
251 "      'm' -- set initially muted\n"
252 "      'M[(<class>)]'\n"
253 "          -- enable music on hold when the conference has a single caller.\n"
254 "             Optionally, specify a musiconhold class to use.  If one is not\n"
255 "             provided, it will use the channel's currently set music class,\n"
256 "             or \"default\".\n"
257 "      'o' -- set talker optimization - treats talkers who aren't speaking as\n"
258 "             being muted, meaning (a) No encode is done on transmission and\n"
259 "             (b) Received audio that is not registered as talking is omitted\n"
260 "             causing no buildup in background noise\n"
261 "      'p[(<keys>)]'\n"
262 "          -- allow user to exit the conference by pressing '#' (default)\n"
263 "             or any of the defined keys. If keys contain '*' this will override\n"
264 "             option 's'. The key used is set to channel variable MEETME_EXIT_KEY.\n"
265 "      'P' -- always prompt for the pin even if it is specified\n"
266 "      'q' -- quiet mode (don't play enter/leave sounds)\n"
267 "      'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
268 "             using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
269 "             meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
270 "             wav.\n"
271 "      's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
272 "      't' -- set talk only mode. (Talk only, no listening)\n"
273 "      'T' -- set talker detection (sent to manager interface and meetme list)\n"
274 "      'w[(<secs>)]'\n"
275 "          -- wait until the marked user enters the conference\n"
276 "      'x' -- close the conference when last marked user exits\n"
277 "      'X' -- allow user to exit the conference by entering a valid single\n"
278 "             digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
279 "             if that variable is not defined.\n"
280 "      '1' -- do not play message when first person enters\n"
281 "      'S(x)' -- Kick the user 'x' seconds *after* he entered into the conference.\n"
282 "      'L(x[:y][:z])' - Limit the conference to 'x' ms. Play a warning when 'y' ms are\n"
283 "             left. Repeat the warning every 'z' ms. The following special\n"
284 "             variables can be used with this option:\n"
285 "              * CONF_LIMIT_TIMEOUT_FILE       File to play when time is up.\n"
286 "              * CONF_LIMIT_WARNING_FILE       File to play as warning if 'y' is defined.\n"
287 "                                              The default is to say the time remaining.\n"
288 "";
289
290 static const char *descrip2 =
291 "  MeetMeCount(confno[,var]): Plays back the number of users in the specified\n"
292 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
293 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
294 "the channel, unless priority n+1 exists, in which case priority progress will\n"
295 "continue.\n"
296 "";
297
298 static const char *descrip3 = 
299 "  MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
300 "      'e' -- Eject last user that joined\n"
301 "      'E' -- Extend conference end time, if scheduled\n"
302 "      'k' -- Kick one user out of conference\n"
303 "      'K' -- Kick all users out of conference\n"
304 "      'l' -- Unlock conference\n"
305 "      'L' -- Lock conference\n"
306 "      'm' -- Unmute one user\n"
307 "      'M' -- Mute one user\n"
308 "      'n' -- Unmute all users in the conference\n"
309 "      'N' -- Mute all non-admin users in the conference\n"
310 "      'r' -- Reset one user's volume settings\n"
311 "      'R' -- Reset all users volume settings\n"
312 "      's' -- Lower entire conference speaking volume\n"
313 "      'S' -- Raise entire conference speaking volume\n"
314 "      't' -- Lower one user's talk volume\n"
315 "      'T' -- Raise one user's talk volume\n"
316 "      'u' -- Lower one user's listen volume\n"
317 "      'U' -- Raise one user's listen volume\n"
318 "      'v' -- Lower entire conference listening volume\n"
319 "      'V' -- Raise entire conference listening volume\n"
320 "  MeetMeAdmin will additionally set the variable MEETMEADMINSTATUS with one\n"
321 "of the following values:\n"
322 "      'NOPARSE'  -- Invalid arguments\n"
323 "      'NOTFOUND' -- User specified was not found\n"
324 "      'FAILED'   -- Another failure occurred\n"
325 "      'OK'       -- The operation was completed successfully\n"
326 "";
327
328 static const char *descrip4 = 
329 "  MeetMeChannelAdmin(channel,command): Run admin command for a specific\n"
330 "channel in any coference.\n"
331 "      'k' -- Kick the specified user out of the conference he is in\n"
332 "      'm' -- Unmute the specified user\n"
333 "      'M' -- Mute the specified user\n"
334 "";
335
336 static const char *slastation_desc =
337 "  SLAStation(<station name>):\n"
338 "This application should be executed by an SLA station.  The argument depends\n"
339 "on how the call was initiated.  If the phone was just taken off hook, then\n"
340 "the argument \"station\" should be just the station name.  If the call was\n"
341 "initiated by pressing a line key, then the station name should be preceded\n"
342 "by an underscore and the trunk name associated with that line button.\n"
343 "For example: \"station1_line1\"."
344 "  On exit, this application will set the variable SLASTATION_STATUS to\n"
345 "one of the following values:\n"
346 "    FAILURE | CONGESTION | SUCCESS\n"
347 "";
348
349 static const char *slatrunk_desc =
350 "  SLATrunk(<trunk name>[,options]):\n"
351 "This application should be executed by an SLA trunk on an inbound call.\n"
352 "The channel calling this application should correspond to the SLA trunk\n"
353 "with the name \"trunk\" that is being passed as an argument.\n"
354 "  On exit, this application will set the variable SLATRUNK_STATUS to\n"
355 "one of the following values:\n"
356 "   FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
357 "  The available options are:\n"
358 "    M[(<class>)]          - Play back the specified MOH class instead of ringing\n"
359 "";
360
361 #define MAX_CONFNUM 80
362 #define MAX_PIN     80
363 #define OPTIONS_LEN 32
364
365 /*! \brief The MeetMe Conference object */
366 struct ast_conference {
367         ast_mutex_t playlock;                   /*!< Conference specific lock (players) */
368         ast_mutex_t listenlock;                 /*!< Conference specific lock (listeners) */
369         char confno[MAX_CONFNUM];               /*!< Conference */
370         struct ast_channel *chan;               /*!< Announcements channel */
371         struct ast_channel *lchan;              /*!< Listen/Record channel */
372         int fd;                                 /*!< Announcements fd */
373         int dahdiconf;                            /*!< DAHDI Conf # */
374         int users;                              /*!< Number of active users */
375         int markedusers;                        /*!< Number of marked users */
376         int maxusers;                           /*!< Participant limit if scheduled */
377         int endalert;                           /*!< When to play conf ending message */
378         time_t start;                           /*!< Start time (s) */
379         int refcount;                           /*!< reference count of usage */
380         enum recording_state recording:2;       /*!< recording status */
381         unsigned int isdynamic:1;               /*!< Created on the fly? */
382         unsigned int locked:1;                  /*!< Is the conference locked? */
383         pthread_t recordthread;                 /*!< thread for recording */
384         ast_mutex_t recordthreadlock;           /*!< control threads trying to start recordthread */
385         pthread_attr_t attr;                    /*!< thread attribute */
386         char *recordingfilename;                /*!< Filename to record the Conference into */
387         char *recordingformat;                  /*!< Format to record the Conference in */
388         char pin[MAX_PIN];                      /*!< If protected by a PIN */
389         char pinadmin[MAX_PIN];                 /*!< If protected by a admin PIN */
390         char uniqueid[32];
391         long endtime;                           /*!< When to end the conf if scheduled */
392         const char *useropts;                   /*!< RealTime user flags */
393         const char *adminopts;                  /*!< RealTime moderator flags */
394         const char *bookid;                     /*!< RealTime conference id */
395         struct ast_frame *transframe[32];
396         struct ast_frame *origframe;
397         struct ast_trans_pvt *transpath[32];
398         AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
399         AST_LIST_ENTRY(ast_conference) list;
400 };
401
402 static AST_LIST_HEAD_STATIC(confs, ast_conference);
403
404 static unsigned int conf_map[1024] = {0, };
405
406 struct volume {
407         int desired;                            /*!< Desired volume adjustment */
408         int actual;                             /*!< Actual volume adjustment (for channels that can't adjust) */
409 };
410
411 /*! \brief The MeetMe User object */
412 struct ast_conf_user {
413         int user_no;                            /*!< User Number */
414         int userflags;                          /*!< Flags as set in the conference */
415         int adminflags;                         /*!< Flags set by the Admin */
416         struct ast_channel *chan;               /*!< Connected channel */
417         int talking;                            /*!< Is user talking */
418         int dahdichannel;                         /*!< Is a DAHDI channel */
419         char usrvalue[50];                      /*!< Custom User Value */
420         char namerecloc[PATH_MAX];                              /*!< Name Recorded file Location */
421         time_t jointime;                        /*!< Time the user joined the conference */
422         time_t kicktime;                        /*!< Time the user will be kicked from the conference */
423         struct timeval start_time;              /*!< Time the user entered into the conference */
424         long timelimit;                         /*!< Time limit for the user to be in the conference L(x:y:z) */
425         long play_warning;                      /*!< Play a warning when 'y' ms are left */
426         long warning_freq;                      /*!< Repeat the warning every 'z' ms */
427         const char *warning_sound;              /*!< File to play as warning if 'y' is defined */
428         const char *end_sound;                  /*!< File to play when time is up. */
429         struct volume talk;
430         struct volume listen;
431         AST_LIST_ENTRY(ast_conf_user) list;
432 };
433
434 enum sla_which_trunk_refs {
435         ALL_TRUNK_REFS,
436         INACTIVE_TRUNK_REFS,
437 };
438
439 enum sla_trunk_state {
440         SLA_TRUNK_STATE_IDLE,
441         SLA_TRUNK_STATE_RINGING,
442         SLA_TRUNK_STATE_UP,
443         SLA_TRUNK_STATE_ONHOLD,
444         SLA_TRUNK_STATE_ONHOLD_BYME,
445 };
446
447 enum sla_hold_access {
448         /*! This means that any station can put it on hold, and any station
449          * can retrieve the call from hold. */
450         SLA_HOLD_OPEN,
451         /*! This means that only the station that put the call on hold may
452          * retrieve it from hold. */
453         SLA_HOLD_PRIVATE,
454 };
455
456 struct sla_trunk_ref;
457
458 struct sla_station {
459         AST_RWLIST_ENTRY(sla_station) entry;
460         AST_DECLARE_STRING_FIELDS(
461                 AST_STRING_FIELD(name); 
462                 AST_STRING_FIELD(device);       
463                 AST_STRING_FIELD(autocontext);  
464         );
465         AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
466         struct ast_dial *dial;
467         /*! Ring timeout for this station, for any trunk.  If a ring timeout
468          *  is set for a specific trunk on this station, that will take
469          *  priority over this value. */
470         unsigned int ring_timeout;
471         /*! Ring delay for this station, for any trunk.  If a ring delay
472          *  is set for a specific trunk on this station, that will take
473          *  priority over this value. */
474         unsigned int ring_delay;
475         /*! This option uses the values in the sla_hold_access enum and sets the
476          * access control type for hold on this station. */
477         unsigned int hold_access:1;
478         /*! Use count for inside sla_station_exec */
479         unsigned int ref_count;
480 };
481
482 struct sla_station_ref {
483         AST_LIST_ENTRY(sla_station_ref) entry;
484         struct sla_station *station;
485 };
486
487 struct sla_trunk {
488         AST_RWLIST_ENTRY(sla_trunk) entry;
489         AST_DECLARE_STRING_FIELDS(
490                 AST_STRING_FIELD(name);
491                 AST_STRING_FIELD(device);
492                 AST_STRING_FIELD(autocontext);  
493         );
494         AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
495         /*! Number of stations that use this trunk */
496         unsigned int num_stations;
497         /*! Number of stations currently on a call with this trunk */
498         unsigned int active_stations;
499         /*! Number of stations that have this trunk on hold. */
500         unsigned int hold_stations;
501         struct ast_channel *chan;
502         unsigned int ring_timeout;
503         /*! If set to 1, no station will be able to join an active call with
504          *  this trunk. */
505         unsigned int barge_disabled:1;
506         /*! This option uses the values in the sla_hold_access enum and sets the
507          * access control type for hold on this trunk. */
508         unsigned int hold_access:1;
509         /*! Whether this trunk is currently on hold, meaning that once a station
510          *  connects to it, the trunk channel needs to have UNHOLD indicated to it. */
511         unsigned int on_hold:1;
512         /*! Use count for inside sla_trunk_exec */
513         unsigned int ref_count;
514 };
515
516 struct sla_trunk_ref {
517         AST_LIST_ENTRY(sla_trunk_ref) entry;
518         struct sla_trunk *trunk;
519         enum sla_trunk_state state;
520         struct ast_channel *chan;
521         /*! Ring timeout to use when this trunk is ringing on this specific
522          *  station.  This takes higher priority than a ring timeout set at
523          *  the station level. */
524         unsigned int ring_timeout;
525         /*! Ring delay to use when this trunk is ringing on this specific
526          *  station.  This takes higher priority than a ring delay set at
527          *  the station level. */
528         unsigned int ring_delay;
529 };
530
531 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
532 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
533
534 static const char sla_registrar[] = "SLA";
535
536 /*! \brief Event types that can be queued up for the SLA thread */
537 enum sla_event_type {
538         /*! A station has put the call on hold */
539         SLA_EVENT_HOLD,
540         /*! The state of a dial has changed */
541         SLA_EVENT_DIAL_STATE,
542         /*! The state of a ringing trunk has changed */
543         SLA_EVENT_RINGING_TRUNK,
544         /*! A reload of configuration has been requested */
545         SLA_EVENT_RELOAD,
546         /*! Poke the SLA thread so it can check if it can perform a reload */
547         SLA_EVENT_CHECK_RELOAD,
548 };
549
550 struct sla_event {
551         enum sla_event_type type;
552         struct sla_station *station;
553         struct sla_trunk_ref *trunk_ref;
554         AST_LIST_ENTRY(sla_event) entry;
555 };
556
557 /*! \brief A station that failed to be dialed 
558  * \note Only used by the SLA thread. */
559 struct sla_failed_station {
560         struct sla_station *station;
561         struct timeval last_try;
562         AST_LIST_ENTRY(sla_failed_station) entry;
563 };
564
565 /*! \brief A trunk that is ringing */
566 struct sla_ringing_trunk {
567         struct sla_trunk *trunk;
568         /*! The time that this trunk started ringing */
569         struct timeval ring_begin;
570         AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
571         AST_LIST_ENTRY(sla_ringing_trunk) entry;
572 };
573
574 enum sla_station_hangup {
575         SLA_STATION_HANGUP_NORMAL,
576         SLA_STATION_HANGUP_TIMEOUT,
577 };
578
579 /*! \brief A station that is ringing */
580 struct sla_ringing_station {
581         struct sla_station *station;
582         /*! The time that this station started ringing */
583         struct timeval ring_begin;
584         AST_LIST_ENTRY(sla_ringing_station) entry;
585 };
586
587 /*!
588  * \brief A structure for data used by the sla thread
589  */
590 static struct {
591         /*! The SLA thread ID */
592         pthread_t thread;
593         ast_cond_t cond;
594         ast_mutex_t lock;
595         AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
596         AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
597         AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
598         AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
599         unsigned int stop:1;
600         /*! Attempt to handle CallerID, even though it is known not to work
601          *  properly in some situations. */
602         unsigned int attempt_callerid:1;
603         /*! A reload has been requested */
604         unsigned int reload:1;
605 } sla = {
606         .thread = AST_PTHREADT_NULL,
607 };
608
609 /*! The number of audio buffers to be allocated on pseudo channels
610  *  when in a conference */
611 static int audio_buffers;
612
613 /*! Map 'volume' levels from -5 through +5 into
614  *  decibel (dB) settings for channel drivers
615  *  Note: these are not a straight linear-to-dB
616  *  conversion... the numbers have been modified
617  *  to give the user a better level of adjustability
618  */
619 static char const gain_map[] = {
620         -15,
621         -13,
622         -10,
623         -6,
624         0,
625         0,
626         0,
627         6,
628         10,
629         13,
630         15,
631 };
632
633
634 static int admin_exec(struct ast_channel *chan, void *data);
635 static void *recordthread(void *args);
636
637 static char *istalking(int x)
638 {
639         if (x > 0)
640                 return "(talking)";
641         else if (x < 0)
642                 return "(unmonitored)";
643         else 
644                 return "(not talking)";
645 }
646
647 static int careful_write(int fd, unsigned char *data, int len, int block)
648 {
649         int res;
650         int x;
651
652         while (len) {
653                 if (block) {
654                         x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
655                         res = ioctl(fd, DAHDI_IOMUX, &x);
656                 } else
657                         res = 0;
658                 if (res >= 0)
659                         res = write(fd, data, len);
660                 if (res < 1) {
661                         if (errno != EAGAIN) {
662                                 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
663                                 return -1;
664                         } else
665                                 return 0;
666                 }
667                 len -= res;
668                 data += res;
669         }
670
671         return 0;
672 }
673
674 static int set_talk_volume(struct ast_conf_user *user, int volume)
675 {
676         char gain_adjust;
677
678         /* attempt to make the adjustment in the channel driver;
679            if successful, don't adjust in the frame reading routine
680         */
681         gain_adjust = gain_map[volume + 5];
682
683         return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
684 }
685
686 static int set_listen_volume(struct ast_conf_user *user, int volume)
687 {
688         char gain_adjust;
689
690         /* attempt to make the adjustment in the channel driver;
691            if successful, don't adjust in the frame reading routine
692         */
693         gain_adjust = gain_map[volume + 5];
694
695         return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
696 }
697
698 static void tweak_volume(struct volume *vol, enum volume_action action)
699 {
700         switch (action) {
701         case VOL_UP:
702                 switch (vol->desired) { 
703                 case 5:
704                         break;
705                 case 0:
706                         vol->desired = 2;
707                         break;
708                 case -2:
709                         vol->desired = 0;
710                         break;
711                 default:
712                         vol->desired++;
713                         break;
714                 }
715                 break;
716         case VOL_DOWN:
717                 switch (vol->desired) {
718                 case -5:
719                         break;
720                 case 2:
721                         vol->desired = 0;
722                         break;
723                 case 0:
724                         vol->desired = -2;
725                         break;
726                 default:
727                         vol->desired--;
728                         break;
729                 }
730         }
731 }
732
733 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
734 {
735         tweak_volume(&user->talk, action);
736         /* attempt to make the adjustment in the channel driver;
737            if successful, don't adjust in the frame reading routine
738         */
739         if (!set_talk_volume(user, user->talk.desired))
740                 user->talk.actual = 0;
741         else
742                 user->talk.actual = user->talk.desired;
743 }
744
745 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
746 {
747         tweak_volume(&user->listen, action);
748         /* attempt to make the adjustment in the channel driver;
749            if successful, don't adjust in the frame reading routine
750         */
751         if (!set_listen_volume(user, user->listen.desired))
752                 user->listen.actual = 0;
753         else
754                 user->listen.actual = user->listen.desired;
755 }
756
757 static void reset_volumes(struct ast_conf_user *user)
758 {
759         signed char zero_volume = 0;
760
761         ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
762         ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
763 }
764
765 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
766 {
767         unsigned char *data;
768         int len;
769         int res = -1;
770
771         if (!ast_check_hangup(chan))
772                 res = ast_autoservice_start(chan);
773
774         AST_LIST_LOCK(&confs);
775
776         switch(sound) {
777         case ENTER:
778                 data = enter;
779                 len = sizeof(enter);
780                 break;
781         case LEAVE:
782                 data = leave;
783                 len = sizeof(leave);
784                 break;
785         default:
786                 data = NULL;
787                 len = 0;
788         }
789         if (data) {
790                 careful_write(conf->fd, data, len, 1);
791         }
792
793         AST_LIST_UNLOCK(&confs);
794
795         if (!res) 
796                 ast_autoservice_stop(chan);
797 }
798
799 /*!
800  * \brief Find or create a conference
801  *
802  * \param confno The conference name/number
803  * \param pin The regular user pin
804  * \param pinadmin The admin pin
805  * \param make Make the conf if it doesn't exist
806  * \param dynamic Mark the newly created conference as dynamic
807  * \param refcount How many references to mark on the conference
808  * \param chan The asterisk channel
809  *
810  * \return A pointer to the conference struct, or NULL if it wasn't found and
811  *         make or dynamic were not set.
812  */
813 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
814 {
815         struct ast_conference *cnf;
816         struct dahdi_confinfo dahdic = { 0, };
817         int confno_int = 0;
818
819         AST_LIST_LOCK(&confs);
820
821         AST_LIST_TRAVERSE(&confs, cnf, list) {
822                 if (!strcmp(confno, cnf->confno)) 
823                         break;
824         }
825
826         if (cnf || (!make && !dynamic))
827                 goto cnfout;
828
829         /* Make a new one */
830         if (!(cnf = ast_calloc(1, sizeof(*cnf))))
831                 goto cnfout;
832
833         ast_mutex_init(&cnf->playlock);
834         ast_mutex_init(&cnf->listenlock);
835         cnf->recordthread = AST_PTHREADT_NULL;
836         ast_mutex_init(&cnf->recordthreadlock);
837         ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
838         ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
839         ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
840         ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
841
842         /* Setup a new dahdi conference */
843         dahdic.confno = -1;
844         dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
845         cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
846         if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
847                 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
848                 if (cnf->fd >= 0)
849                         close(cnf->fd);
850                 ast_free(cnf);
851                 cnf = NULL;
852                 goto cnfout;
853         }
854
855         cnf->dahdiconf = dahdic.confno;
856
857         /* Setup a new channel for playback of audio files */
858         cnf->chan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL);
859         if (cnf->chan) {
860                 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
861                 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
862                 dahdic.chan = 0;
863                 dahdic.confno = cnf->dahdiconf;
864                 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
865                 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
866                         ast_log(LOG_WARNING, "Error setting conference\n");
867                         if (cnf->chan)
868                                 ast_hangup(cnf->chan);
869                         else
870                                 close(cnf->fd);
871
872                         ast_free(cnf);
873                         cnf = NULL;
874                         goto cnfout;
875                 }
876         }
877
878         /* Fill the conference struct */
879         cnf->start = time(NULL);
880         cnf->maxusers = 0x7fffffff;
881         cnf->isdynamic = dynamic ? 1 : 0;
882         ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
883         AST_LIST_INSERT_HEAD(&confs, cnf, list);
884
885         /* Reserve conference number in map */
886         if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
887                 conf_map[confno_int] = 1;
888         
889 cnfout:
890         if (cnf)
891                 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
892
893         AST_LIST_UNLOCK(&confs);
894
895         return cnf;
896 }
897
898 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
899 {
900         static char *cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
901
902         int len = strlen(word);
903         int which = 0;
904         struct ast_conference *cnf = NULL;
905         struct ast_conf_user *usr = NULL;
906         char *confno = NULL;
907         char usrno[50] = "";
908         char *myline, *ret = NULL;
909         
910         if (pos == 1) {         /* Command */
911                 return ast_cli_complete(word, cmds, state);
912         } else if (pos == 2) {  /* Conference Number */
913                 AST_LIST_LOCK(&confs);
914                 AST_LIST_TRAVERSE(&confs, cnf, list) {
915                         if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
916                                 ret = cnf->confno;
917                                 break;
918                         }
919                 }
920                 ret = ast_strdup(ret); /* dup before releasing the lock */
921                 AST_LIST_UNLOCK(&confs);
922                 return ret;
923         } else if (pos == 3) {
924                 /* User Number || Conf Command option*/
925                 if (strstr(line, "mute") || strstr(line, "kick")) {
926                         if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
927                                 return ast_strdup("all");
928                         which++;
929                         AST_LIST_LOCK(&confs);
930
931                         /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
932                         myline = ast_strdupa(line);
933                         if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
934                                 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
935                                         ;
936                         }
937                         
938                         AST_LIST_TRAVERSE(&confs, cnf, list) {
939                                 if (!strcmp(confno, cnf->confno))
940                                     break;
941                         }
942
943                         if (cnf) {
944                                 /* Search for the user */
945                                 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
946                                         snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
947                                         if (!strncasecmp(word, usrno, len) && ++which > state)
948                                                 break;
949                                 }
950                         }
951                         AST_LIST_UNLOCK(&confs);
952                         return usr ? ast_strdup(usrno) : NULL;
953                 }
954         }
955
956         return NULL;
957 }
958
959 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
960 {
961         /* Process the command */
962         struct ast_conf_user *user;
963         struct ast_conference *cnf;
964         int hr, min, sec;
965         int i = 0, total = 0;
966         time_t now;
967         struct ast_str *cmdline = NULL;
968 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s  %-8s  %-6s\n"
969 #define MC_DATA_FORMAT "%-12.12s   %4.4d              %4.4s       %02d:%02d:%02d  %-8s  %-6s\n"
970
971         switch (cmd) {
972         case CLI_INIT:
973                 e->command = "meetme list [concise]";
974                 e->usage =
975                         "Usage: meetme list [concise] <confno> \n"
976                         "       List all or a specific conference.\n";
977                 return NULL;
978         case CLI_GENERATE:
979                 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
980         }
981
982         /* Check for length so no buffer will overflow... */
983         for (i = 0; i < a->argc; i++) {
984                 if (strlen(a->argv[i]) > 100)
985                         ast_cli(a->fd, "Invalid Arguments.\n");
986         }
987
988         /* Max confno length */
989         if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
990                 return CLI_FAILURE;
991         }
992
993         if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], "concise"))) {
994                 /* List all the conferences */  
995                 int concise = (a->argc == 3 && !strcasecmp(a->argv[2], "concise"));
996                 now = time(NULL);
997                 AST_LIST_LOCK(&confs);
998                 if (AST_LIST_EMPTY(&confs)) {
999                         if (!concise) {
1000                                 ast_cli(a->fd, "No active MeetMe conferences.\n");
1001                         }
1002                         AST_LIST_UNLOCK(&confs);
1003                         ast_free(cmdline);
1004                         return CLI_SUCCESS;
1005                 }
1006                 if (!concise) {
1007                         ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1008                 }
1009                 AST_LIST_TRAVERSE(&confs, cnf, list) {
1010                         if (cnf->markedusers == 0) {
1011                                 ast_str_set(&cmdline, 0, "N/A ");
1012                         } else {
1013                                 ast_str_set(&cmdline, 0, "%4.4d", cnf->markedusers);
1014                         }
1015                         hr = (now - cnf->start) / 3600;
1016                         min = ((now - cnf->start) % 3600) / 60;
1017                         sec = (now - cnf->start) % 60;
1018                         if (!concise) {
1019                                 ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users, cmdline->str, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
1020                         } else {
1021                                 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1022                                         cnf->confno,
1023                                         cnf->users,
1024                                         cnf->markedusers,
1025                                         hr, min, sec,
1026                                         cnf->isdynamic,
1027                                         cnf->locked);
1028                         }
1029
1030                         total += cnf->users;
1031                 }
1032                 AST_LIST_UNLOCK(&confs);
1033                 if (!concise) {
1034                         ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1035                 }
1036                 ast_free(cmdline);
1037                 return CLI_SUCCESS;
1038         } else if (strcmp(a->argv[1], "list") == 0) {
1039                 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
1040                 /* List all the users in a conference */
1041                 if (AST_LIST_EMPTY(&confs)) {
1042                         if (!concise) {
1043                                 ast_cli(a->fd, "No active MeetMe conferences.\n");
1044                         }
1045                         ast_free(cmdline);
1046                         return CLI_SUCCESS;     
1047                 }
1048                 /* Find the right conference */
1049                 AST_LIST_LOCK(&confs);
1050                 AST_LIST_TRAVERSE(&confs, cnf, list) {
1051                         if (strcmp(cnf->confno, a->argv[2]) == 0) {
1052                                 break;
1053                         }
1054                 }
1055                 if (!cnf) {
1056                         if (!concise)
1057                                 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1058                         AST_LIST_UNLOCK(&confs);
1059                         ast_free(cmdline);
1060                         return CLI_SUCCESS;
1061                 }
1062                 /* Show all the users */
1063                 time(&now);
1064                 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
1065                         hr = (now - user->jointime) / 3600;
1066                         min = ((now - user->jointime) % 3600) / 60;
1067                         sec = (now - user->jointime) % 60;
1068                         if (!concise) {
1069                                 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1070                                         user->user_no,
1071                                         S_OR(user->chan->cid.cid_num, "<unknown>"),
1072                                         S_OR(user->chan->cid.cid_name, "<no name>"),
1073                                         user->chan->name,
1074                                         user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
1075                                         user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
1076                                         user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1077                                         user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1078                                         istalking(user->talking), hr, min, sec); 
1079                         } else {
1080                                 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1081                                         user->user_no,
1082                                         S_OR(user->chan->cid.cid_num, ""),
1083                                         S_OR(user->chan->cid.cid_name, ""),
1084                                         user->chan->name,
1085                                         user->userflags  & CONFFLAG_ADMIN   ? "1" : "",
1086                                         user->userflags  & CONFFLAG_MONITOR ? "1" : "",
1087                                         user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1088                                         user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1089                                         user->talking, hr, min, sec);
1090                         }
1091                 }
1092                 if (!concise) {
1093                         ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1094                 }
1095                 AST_LIST_UNLOCK(&confs);
1096                 ast_free(cmdline);
1097                 return CLI_SUCCESS;
1098         }
1099         if (a->argc < 2) {
1100                 ast_free(cmdline);
1101                 return CLI_SHOWUSAGE;
1102         }
1103
1104         ast_debug(1, "Cmdline: %s\n", cmdline->str);
1105
1106         admin_exec(NULL, cmdline->str);
1107         ast_free(cmdline);
1108
1109         return CLI_SUCCESS;
1110 }
1111
1112
1113 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1114 {
1115         /* Process the command */
1116         struct ast_str *cmdline = NULL;
1117         int i = 0;
1118
1119         switch (cmd) {
1120         case CLI_INIT:
1121                 e->command = "meetme {lock|unlock|mute|unmute|kick}";
1122                 e->usage =
1123                         "Usage: meetme (un)lock|(un)mute|kick <confno> <usernumber>\n"
1124                         "       Executes a command for the conference or on a conferee\n";
1125                 return NULL;
1126         case CLI_GENERATE:
1127                 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1128         }
1129
1130         if (a->argc > 8)
1131                 ast_cli(a->fd, "Invalid Arguments.\n");
1132         /* Check for length so no buffer will overflow... */
1133         for (i = 0; i < a->argc; i++) {
1134                 if (strlen(a->argv[i]) > 100)
1135                         ast_cli(a->fd, "Invalid Arguments.\n");
1136         }
1137
1138         /* Max confno length */
1139         if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1140                 return CLI_FAILURE;
1141         }
1142
1143         if (a->argc < 1) {
1144                 ast_free(cmdline);
1145                 return CLI_SHOWUSAGE;
1146         }
1147
1148         ast_str_set(&cmdline, 0, "%s", a->argv[2]);     /* Argv 2: conference number */
1149         if (strstr(a->argv[1], "lock")) {
1150                 if (strcmp(a->argv[1], "lock") == 0) {
1151                         /* Lock */
1152                         ast_str_append(&cmdline, 0, ",L");
1153                 } else {
1154                         /* Unlock */
1155                         ast_str_append(&cmdline, 0, ",l");
1156                 }
1157         } else if (strstr(a->argv[1], "mute")) { 
1158                 if (a->argc < 4) {
1159                         ast_free(cmdline);
1160                         return CLI_SHOWUSAGE;
1161                 }
1162                 if (strcmp(a->argv[1], "mute") == 0) {
1163                         /* Mute */
1164                         if (strcmp(a->argv[3], "all") == 0) {
1165                                 ast_str_append(&cmdline, 0, ",N");
1166                         } else {
1167                                 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);       
1168                         }
1169                 } else {
1170                         /* Unmute */
1171                         if (strcmp(a->argv[3], "all") == 0) {
1172                                 ast_str_append(&cmdline, 0, ",n");
1173                         } else {
1174                                 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
1175                         }
1176                 }
1177         } else if (strcmp(a->argv[1], "kick") == 0) {
1178                 if (a->argc < 4) {
1179                         ast_free(cmdline);
1180                         return CLI_SHOWUSAGE;
1181                 }
1182                 if (strcmp(a->argv[3], "all") == 0) {
1183                         /* Kick all */
1184                         ast_str_append(&cmdline, 0, ",K");
1185                 } else {
1186                         /* Kick a single user */
1187                         ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
1188                 }
1189         } else {
1190                 ast_free(cmdline);
1191                 return CLI_SHOWUSAGE;
1192         }
1193
1194         ast_debug(1, "Cmdline: %s\n", cmdline->str);
1195
1196         admin_exec(NULL, cmdline->str);
1197         ast_free(cmdline);
1198
1199         return CLI_SUCCESS;
1200 }
1201
1202 static const char *sla_hold_str(unsigned int hold_access)
1203 {
1204         const char *hold = "Unknown";
1205
1206         switch (hold_access) {
1207         case SLA_HOLD_OPEN:
1208                 hold = "Open";
1209                 break;
1210         case SLA_HOLD_PRIVATE:
1211                 hold = "Private";
1212         default:
1213                 break;
1214         }
1215
1216         return hold;
1217 }
1218
1219 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1220 {
1221         const struct sla_trunk *trunk;
1222
1223         switch (cmd) {
1224         case CLI_INIT:
1225                 e->command = "sla show trunks";
1226                 e->usage =
1227                         "Usage: sla show trunks\n"
1228                         "       This will list all trunks defined in sla.conf\n";
1229                 return NULL;
1230         case CLI_GENERATE:
1231                 return NULL;
1232         }
1233
1234         ast_cli(a->fd, "\n"
1235                     "=============================================================\n"
1236                     "=== Configured SLA Trunks ===================================\n"
1237                     "=============================================================\n"
1238                     "===\n");
1239         AST_RWLIST_RDLOCK(&sla_trunks);
1240         AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1241                 struct sla_station_ref *station_ref;
1242                 char ring_timeout[16] = "(none)";
1243                 if (trunk->ring_timeout)
1244                         snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1245                 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1246                             "=== Trunk Name:       %s\n"
1247                             "=== ==> Device:       %s\n"
1248                             "=== ==> AutoContext:  %s\n"
1249                             "=== ==> RingTimeout:  %s\n"
1250                             "=== ==> BargeAllowed: %s\n"
1251                             "=== ==> HoldAccess:   %s\n"
1252                             "=== ==> Stations ...\n",
1253                             trunk->name, trunk->device, 
1254                             S_OR(trunk->autocontext, "(none)"), 
1255                             ring_timeout,
1256                             trunk->barge_disabled ? "No" : "Yes",
1257                             sla_hold_str(trunk->hold_access));
1258                 AST_RWLIST_RDLOCK(&sla_stations);
1259                 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1260                         ast_cli(a->fd, "===    ==> Station name: %s\n", station_ref->station->name);
1261                 AST_RWLIST_UNLOCK(&sla_stations);
1262                 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1263         }
1264         AST_RWLIST_UNLOCK(&sla_trunks);
1265         ast_cli(a->fd, "=============================================================\n\n");
1266
1267         return CLI_SUCCESS;
1268 }
1269
1270 static const char *trunkstate2str(enum sla_trunk_state state)
1271 {
1272 #define S(e) case e: return # e;
1273         switch (state) {
1274         S(SLA_TRUNK_STATE_IDLE)
1275         S(SLA_TRUNK_STATE_RINGING)
1276         S(SLA_TRUNK_STATE_UP)
1277         S(SLA_TRUNK_STATE_ONHOLD)
1278         S(SLA_TRUNK_STATE_ONHOLD_BYME)
1279         }
1280         return "Uknown State";
1281 #undef S
1282 }
1283
1284 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1285 {
1286         const struct sla_station *station;
1287
1288         switch (cmd) {
1289         case CLI_INIT:
1290                 e->command = "sla show stations";
1291                 e->usage =
1292                         "Usage: sla show stations\n"
1293                         "       This will list all stations defined in sla.conf\n";
1294                 return NULL;
1295         case CLI_GENERATE:
1296                 return NULL;
1297         }
1298
1299         ast_cli(a->fd, "\n" 
1300                     "=============================================================\n"
1301                     "=== Configured SLA Stations =================================\n"
1302                     "=============================================================\n"
1303                     "===\n");
1304         AST_RWLIST_RDLOCK(&sla_stations);
1305         AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1306                 struct sla_trunk_ref *trunk_ref;
1307                 char ring_timeout[16] = "(none)";
1308                 char ring_delay[16] = "(none)";
1309                 if (station->ring_timeout) {
1310                         snprintf(ring_timeout, sizeof(ring_timeout), 
1311                                 "%u", station->ring_timeout);
1312                 }
1313                 if (station->ring_delay) {
1314                         snprintf(ring_delay, sizeof(ring_delay), 
1315                                 "%u", station->ring_delay);
1316                 }
1317                 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1318                             "=== Station Name:    %s\n"
1319                             "=== ==> Device:      %s\n"
1320                             "=== ==> AutoContext: %s\n"
1321                             "=== ==> RingTimeout: %s\n"
1322                             "=== ==> RingDelay:   %s\n"
1323                             "=== ==> HoldAccess:  %s\n"
1324                             "=== ==> Trunks ...\n",
1325                             station->name, station->device,
1326                             S_OR(station->autocontext, "(none)"), 
1327                             ring_timeout, ring_delay,
1328                             sla_hold_str(station->hold_access));
1329                 AST_RWLIST_RDLOCK(&sla_trunks);
1330                 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1331                         if (trunk_ref->ring_timeout) {
1332                                 snprintf(ring_timeout, sizeof(ring_timeout),
1333                                         "%u", trunk_ref->ring_timeout);
1334                         } else
1335                                 strcpy(ring_timeout, "(none)");
1336                         if (trunk_ref->ring_delay) {
1337                                 snprintf(ring_delay, sizeof(ring_delay),
1338                                         "%u", trunk_ref->ring_delay);
1339                         } else
1340                                 strcpy(ring_delay, "(none)");
1341                                 ast_cli(a->fd, "===    ==> Trunk Name: %s\n"
1342                                     "===       ==> State:       %s\n"
1343                                     "===       ==> RingTimeout: %s\n"
1344                                     "===       ==> RingDelay:   %s\n",
1345                                     trunk_ref->trunk->name,
1346                                     trunkstate2str(trunk_ref->state),
1347                                     ring_timeout, ring_delay);
1348                 }
1349                 AST_RWLIST_UNLOCK(&sla_trunks);
1350                 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1351                             "===\n");
1352         }
1353         AST_RWLIST_UNLOCK(&sla_stations);
1354         ast_cli(a->fd, "============================================================\n"
1355                     "\n");
1356
1357         return CLI_SUCCESS;
1358 }
1359
1360 static struct ast_cli_entry cli_meetme[] = {
1361         AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1362         AST_CLI_DEFINE(meetme_show_cmd, "List all or one conference"),
1363         AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1364         AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1365 };
1366
1367 static void conf_flush(int fd, struct ast_channel *chan)
1368 {
1369         int x;
1370
1371         /* read any frames that may be waiting on the channel
1372            and throw them away
1373         */
1374         if (chan) {
1375                 struct ast_frame *f;
1376
1377                 /* when no frames are available, this will wait
1378                    for 1 millisecond maximum
1379                 */
1380                 while (ast_waitfor(chan, 1)) {
1381                         f = ast_read(chan);
1382                         if (f)
1383                                 ast_frfree(f);
1384                         else /* channel was hung up or something else happened */
1385                                 break;
1386                 }
1387         }
1388
1389         /* flush any data sitting in the pseudo channel */
1390         x = DAHDI_FLUSH_ALL;
1391         if (ioctl(fd, DAHDI_FLUSH, &x))
1392                 ast_log(LOG_WARNING, "Error flushing channel\n");
1393
1394 }
1395
1396 /* Remove the conference from the list and free it.
1397    We assume that this was called while holding conflock. */
1398 static int conf_free(struct ast_conference *conf)
1399 {
1400         int x;
1401         
1402         AST_LIST_REMOVE(&confs, conf, list);
1403         manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1404
1405         if (conf->recording == MEETME_RECORD_ACTIVE) {
1406                 conf->recording = MEETME_RECORD_TERMINATE;
1407                 AST_LIST_UNLOCK(&confs);
1408                 while (1) {
1409                         usleep(1);
1410                         AST_LIST_LOCK(&confs);
1411                         if (conf->recording == MEETME_RECORD_OFF)
1412                                 break;
1413                         AST_LIST_UNLOCK(&confs);
1414                 }
1415         }
1416
1417         for (x = 0; x < AST_FRAME_BITS; x++) {
1418                 if (conf->transframe[x])
1419                         ast_frfree(conf->transframe[x]);
1420                 if (conf->transpath[x])
1421                         ast_translator_free_path(conf->transpath[x]);
1422         }
1423         if (conf->origframe)
1424                 ast_frfree(conf->origframe);
1425         if (conf->lchan)
1426                 ast_hangup(conf->lchan);
1427         if (conf->chan)
1428                 ast_hangup(conf->chan);
1429         if (conf->fd >= 0)
1430                 close(conf->fd);
1431         if (conf->recordingfilename) {
1432                 ast_free(conf->recordingfilename);
1433         }
1434         if (conf->recordingformat) {
1435                 ast_free(conf->recordingformat);
1436         }
1437         ast_mutex_destroy(&conf->playlock);
1438         ast_mutex_destroy(&conf->listenlock);
1439         ast_mutex_destroy(&conf->recordthreadlock);
1440         ast_free(conf);
1441
1442         return 0;
1443 }
1444
1445 static void conf_queue_dtmf(const struct ast_conference *conf,
1446         const struct ast_conf_user *sender, struct ast_frame *f)
1447 {
1448         struct ast_conf_user *user;
1449
1450         AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1451                 if (user == sender)
1452                         continue;
1453                 if (ast_write(user->chan, f) < 0)
1454                         ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1455         }
1456 }
1457
1458 static void sla_queue_event_full(enum sla_event_type type, 
1459         struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1460 {
1461         struct sla_event *event;
1462
1463         if (!(event = ast_calloc(1, sizeof(*event))))
1464                 return;
1465
1466         event->type = type;
1467         event->trunk_ref = trunk_ref;
1468         event->station = station;
1469
1470         if (!lock) {
1471                 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1472                 return;
1473         }
1474
1475         ast_mutex_lock(&sla.lock);
1476         AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1477         ast_cond_signal(&sla.cond);
1478         ast_mutex_unlock(&sla.lock);
1479 }
1480
1481 static void sla_queue_event_nolock(enum sla_event_type type)
1482 {
1483         sla_queue_event_full(type, NULL, NULL, 0);
1484 }
1485
1486 static void sla_queue_event(enum sla_event_type type)
1487 {
1488         sla_queue_event_full(type, NULL, NULL, 1);
1489 }
1490
1491 /*! \brief Queue a SLA event from the conference */
1492 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1493         struct ast_conference *conf)
1494 {
1495         struct sla_station *station;
1496         struct sla_trunk_ref *trunk_ref = NULL;
1497         char *trunk_name;
1498
1499         trunk_name = ast_strdupa(conf->confno);
1500         strsep(&trunk_name, "_");
1501         if (ast_strlen_zero(trunk_name)) {
1502                 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1503                 return;
1504         }
1505
1506         AST_RWLIST_RDLOCK(&sla_stations);
1507         AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1508                 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1509                         if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1510                                 break;
1511                 }
1512                 if (trunk_ref)
1513                         break;
1514         }
1515         AST_RWLIST_UNLOCK(&sla_stations);
1516
1517         if (!trunk_ref) {
1518                 ast_debug(1, "Trunk not found for event!\n");
1519                 return;
1520         }
1521
1522         sla_queue_event_full(type, trunk_ref, station, 1);
1523 }
1524
1525 /* Decrement reference counts, as incremented by find_conf() */
1526 static int dispose_conf(struct ast_conference *conf)
1527 {
1528         int res = 0;
1529         int confno_int = 0;
1530
1531         AST_LIST_LOCK(&confs);
1532         if (ast_atomic_dec_and_test(&conf->refcount)) {
1533                 /* Take the conference room number out of an inuse state */
1534                 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
1535                         conf_map[confno_int] = 0;
1536                 }
1537                 conf_free(conf);
1538                 res = 1;
1539         }
1540         AST_LIST_UNLOCK(&confs);
1541
1542         return res;
1543 }
1544
1545 static int rt_extend_conf(char *confno)
1546 {
1547         char currenttime[32];
1548         char endtime[32];
1549         struct timeval now;
1550         struct ast_tm tm;
1551         struct ast_variable *var;
1552         char bookid[8]; 
1553
1554         if (!extendby) {
1555                 return 0;
1556         }
1557
1558         now = ast_tvnow();
1559
1560         ast_localtime(&now, &tm, NULL);
1561         ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1562
1563         var = ast_load_realtime("meetme", "confno",
1564                 confno, "startTime<= ", currenttime,
1565                 "endtime>= ", currenttime, NULL);
1566
1567         /* Identify the specific RealTime conference */
1568         while (var) {
1569                 if (!strcasecmp(var->name, "bookid")) {
1570                         ast_copy_string(bookid, var->value, sizeof(bookid));
1571                 }
1572                 if (!strcasecmp(var->name, "endtime")) {
1573                         ast_copy_string(endtime, var->value, sizeof(endtime));
1574                 }
1575
1576                 var = var->next;
1577         }
1578         ast_variables_destroy(var);
1579
1580         ast_strptime(endtime, DATE_FORMAT, &tm);
1581         now = ast_mktime(&tm, NULL);
1582
1583         now.tv_sec += extendby;
1584
1585         ast_localtime(&now, &tm, NULL);
1586         ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1587         strcat(currenttime, "0"); /* Seconds needs to be 00 */
1588
1589         var = ast_load_realtime("meetme", "confno",
1590                 confno, "startTime<= ", currenttime,
1591                 "endtime>= ", currenttime, NULL);
1592
1593         /* If there is no conflict with extending the conference, update the DB */
1594         if (!var) {
1595                 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
1596                 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
1597                 return 0;
1598
1599         }
1600
1601         ast_variables_destroy(var);
1602         return -1;
1603 }
1604
1605 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
1606 {
1607         char *original_moh;
1608
1609         ast_channel_lock(chan);
1610         original_moh = ast_strdupa(chan->musicclass);
1611         ast_string_field_set(chan, musicclass, musicclass);
1612         ast_channel_unlock(chan);
1613
1614         ast_moh_start(chan, original_moh, NULL);
1615
1616         ast_channel_lock(chan);
1617         ast_string_field_set(chan, musicclass, original_moh);
1618         ast_channel_unlock(chan);
1619 }
1620
1621 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1622 {
1623         struct ast_conf_user *user = NULL;
1624         struct ast_conf_user *usr = NULL;
1625         int fd;
1626         struct dahdi_confinfo dahdic, dahdic_empty;
1627         struct ast_frame *f;
1628         struct ast_channel *c;
1629         struct ast_frame fr;
1630         int outfd;
1631         int ms;
1632         int nfds;
1633         int res;
1634         int flags;
1635         int retrydahdi;
1636         int origfd;
1637         int musiconhold = 0;
1638         int firstpass = 0;
1639         int lastmarked = 0;
1640         int currentmarked = 0;
1641         int ret = -1;
1642         int x;
1643         int menu_active = 0;
1644         int talkreq_manager = 0;
1645         int using_pseudo = 0;
1646         int duration = 20;
1647         int hr, min, sec;
1648         int sent_event = 0;
1649         int checked = 0;
1650         int announcement_played = 0;
1651         struct timeval now;
1652         struct ast_dsp *dsp = NULL;
1653         struct ast_app *agi_app;
1654         char *agifile;
1655         const char *agifiledefault = "conf-background.agi", *tmpvar;
1656         char meetmesecs[30] = "";
1657         char exitcontext[AST_MAX_CONTEXT] = "";
1658         char recordingtmp[AST_MAX_EXTENSION] = "";
1659         char members[10] = "";
1660         int dtmf, opt_waitmarked_timeout = 0;
1661         time_t timeout = 0;
1662         struct dahdi_bufferinfo bi;
1663         char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1664         char *buf = __buf + AST_FRIENDLY_OFFSET;
1665         char *exitkeys = NULL;
1666         unsigned int calldurationlimit = 0;
1667         long timelimit = 0;
1668         long play_warning = 0;
1669         long warning_freq = 0;
1670         const char *warning_sound = NULL;
1671         const char *end_sound = NULL;
1672         char *parse;    
1673         long time_left_ms = 0;
1674         struct timeval nexteventts = { 0, };
1675         int to;
1676         int setusercount = 0;
1677
1678         if (!(user = ast_calloc(1, sizeof(*user))))
1679                 return ret;
1680
1681         /* Possible timeout waiting for marked user */
1682         if ((confflags & CONFFLAG_WAITMARKED) &&
1683                 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1684                 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1685                 (opt_waitmarked_timeout > 0)) {
1686                 timeout = time(NULL) + opt_waitmarked_timeout;
1687         }
1688                 
1689         if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
1690                 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
1691                 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
1692         }
1693         
1694         if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
1695                 char *limit_str, *warning_str, *warnfreq_str;
1696                 const char *var;
1697  
1698                 parse = optargs[OPT_ARG_DURATION_LIMIT];
1699                 limit_str = strsep(&parse, ":");
1700                 warning_str = strsep(&parse, ":");
1701                 warnfreq_str = parse;
1702  
1703                 timelimit = atol(limit_str);
1704                 if (warning_str)
1705                         play_warning = atol(warning_str);
1706                 if (warnfreq_str)
1707                         warning_freq = atol(warnfreq_str);
1708  
1709                 if (!timelimit) {
1710                         timelimit = play_warning = warning_freq = 0;
1711                         warning_sound = NULL;
1712                 } else if (play_warning > timelimit) {                  
1713                         if (!warning_freq) {
1714                                 play_warning = 0;
1715                         } else {
1716                                 while (play_warning > timelimit)
1717                                         play_warning -= warning_freq;
1718                                 if (play_warning < 1)
1719                                         play_warning = warning_freq = 0;
1720                         }
1721                 }
1722                 
1723                 ast_channel_lock(chan);
1724                 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
1725                         var = ast_strdupa(var);
1726                 }
1727                 ast_channel_unlock(chan);
1728
1729                 warning_sound = var ? var : "timeleft";
1730                 
1731                 ast_channel_lock(chan);
1732                 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
1733                         var = ast_strdupa(var);
1734                 }
1735                 ast_channel_unlock(chan);
1736                 
1737                 end_sound = var ? var : NULL;
1738                         
1739                 /* undo effect of S(x) in case they are both used */
1740                 calldurationlimit = 0;
1741                 /* more efficient do it like S(x) does since no advanced opts */
1742                 if (!play_warning && !end_sound && timelimit) { 
1743                         calldurationlimit = timelimit / 1000;
1744                         timelimit = play_warning = warning_freq = 0;
1745                 } else {
1746                         ast_debug(2, "Limit Data for this call:\n");
1747                         ast_debug(2, "- timelimit     = %ld\n", timelimit);
1748                         ast_debug(2, "- play_warning  = %ld\n", play_warning);
1749                         ast_debug(2, "- warning_freq  = %ld\n", warning_freq);
1750                         ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
1751                         ast_debug(2, "- end_sound     = %s\n", end_sound ? end_sound : "UNDEF");
1752                 }
1753         }
1754
1755         /* Get exit keys */
1756         if ((confflags & CONFFLAG_KEYEXIT)) {
1757                 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1758                         exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1759                 else
1760                         exitkeys = ast_strdupa("#"); /* Default */
1761         }
1762         
1763         if (confflags & CONFFLAG_RECORDCONF) {
1764                 if (!conf->recordingfilename) {
1765                         const char *var;
1766                         ast_channel_lock(chan);
1767                         if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
1768                                 conf->recordingfilename = ast_strdup(var);
1769                         }
1770                         if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
1771                                 conf->recordingformat = ast_strdup(var);
1772                         }
1773                         ast_channel_unlock(chan);
1774                         if (!conf->recordingfilename) {
1775                                 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1776                                 conf->recordingfilename = ast_strdup(recordingtmp);
1777                         }
1778                         if (!conf->recordingformat) {
1779                                 conf->recordingformat = ast_strdup("wav");
1780                         }
1781                         ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1782                                     conf->confno, conf->recordingfilename, conf->recordingformat);
1783                 }
1784         }
1785
1786         ast_mutex_lock(&conf->recordthreadlock);
1787         if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1788                 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1789                 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1790                 dahdic.chan = 0;
1791                 dahdic.confno = conf->dahdiconf;
1792                 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1793                 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
1794                         ast_log(LOG_WARNING, "Error starting listen channel\n");
1795                         ast_hangup(conf->lchan);
1796                         conf->lchan = NULL;
1797                 } else {
1798                         ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1799                 }
1800         }
1801         ast_mutex_unlock(&conf->recordthreadlock);
1802
1803         time(&user->jointime);
1804         
1805         user->timelimit = timelimit;
1806         user->play_warning = play_warning;
1807         user->warning_freq = warning_freq;
1808         user->warning_sound = warning_sound;
1809         user->end_sound = end_sound;    
1810         
1811         if (calldurationlimit > 0) {
1812                 time(&user->kicktime);
1813                 user->kicktime = user->kicktime + calldurationlimit;
1814         }
1815         
1816         if (ast_tvzero(user->start_time))
1817                 user->start_time = ast_tvnow();
1818         time_left_ms = user->timelimit;
1819         
1820         if (user->timelimit) {
1821                 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
1822                 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
1823         }
1824
1825         if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1826                 /* Sorry, but this conference is locked! */     
1827                 if (!ast_streamfile(chan, "conf-locked", chan->language))
1828                         ast_waitstream(chan, "");
1829                 goto outrun;
1830         }
1831
1832         ast_mutex_lock(&conf->playlock);
1833
1834         if (AST_LIST_EMPTY(&conf->userlist))
1835                 user->user_no = 1;
1836         else
1837                 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1838
1839         if (rt_schedule && conf->maxusers)
1840                 if (user->user_no > conf->maxusers) {
1841                         /* Sorry, but this confernce has reached the participant limit! */      
1842                         if (!ast_streamfile(chan, "conf-full", chan->language))
1843                                 ast_waitstream(chan, "");
1844                         goto outrun;
1845                 }
1846
1847         AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1848
1849         user->chan = chan;
1850         user->userflags = confflags;
1851         user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1852         user->talking = -1;
1853
1854         ast_mutex_unlock(&conf->playlock);
1855
1856         if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1857                 char destdir[PATH_MAX];
1858
1859                 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
1860
1861                 if (ast_mkdir(destdir, 0777) != 0) {
1862                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
1863                         goto outrun;
1864                 }
1865
1866                 snprintf(user->namerecloc, sizeof(user->namerecloc),
1867                          "%s/meetme-username-%s-%d", destdir,
1868                          conf->confno, user->user_no);
1869                 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1870                         res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
1871                 else
1872                         res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1873                 if (res == -1)
1874                         goto outrun;
1875         }
1876
1877         ast_mutex_lock(&conf->playlock);
1878
1879         if (confflags & CONFFLAG_MARKEDUSER)
1880                 conf->markedusers++;
1881         conf->users++;
1882         if (rt_log_members) {
1883                 /* Update table */
1884                 snprintf(members, sizeof(members), "%d", conf->users);
1885                 ast_realtime_require_field("meetme",
1886                         "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
1887                         "members", RQ_UINTEGER1, strlen(members),
1888                         NULL);
1889                 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1890         }
1891         setusercount = 1;
1892
1893         /* This device changed state now - if this is the first user */
1894         if (conf->users == 1)
1895                 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
1896
1897         ast_mutex_unlock(&conf->playlock);
1898
1899         /* return the unique ID of the conference */
1900         pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
1901
1902         if (confflags & CONFFLAG_EXIT_CONTEXT) {
1903                 ast_channel_lock(chan);
1904                 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
1905                         ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
1906                 } else if (!ast_strlen_zero(chan->macrocontext)) {
1907                         ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1908                 } else {
1909                         ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1910                 }
1911                 ast_channel_unlock(chan);
1912         }
1913
1914         if (!(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
1915                 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1916                         if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1917                                 ast_waitstream(chan, "");
1918                 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1919                         if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1920                                 ast_waitstream(chan, "");
1921         }
1922
1923         if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1924                 int keepplaying = 1;
1925
1926                 if (conf->users == 2) { 
1927                         if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
1928                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
1929                                 ast_stopstream(chan);
1930                                 if (res > 0)
1931                                         keepplaying = 0;
1932                                 else if (res == -1)
1933                                         goto outrun;
1934                         }
1935                 } else { 
1936                         if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1937                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
1938                                 ast_stopstream(chan);
1939                                 if (res > 0)
1940                                         keepplaying = 0;
1941                                 else if (res == -1)
1942                                         goto outrun;
1943                         }
1944                         if (keepplaying) {
1945                                 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1946                                 if (res > 0)
1947                                         keepplaying = 0;
1948                                 else if (res == -1)
1949                                         goto outrun;
1950                         }
1951                         if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1952                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
1953                                 ast_stopstream(chan);
1954                                 if (res > 0)
1955                                         keepplaying = 0;
1956                                 else if (res == -1) 
1957                                         goto outrun;
1958                         }
1959                 }
1960         }
1961
1962         ast_indicate(chan, -1);
1963
1964         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1965                 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1966                 goto outrun;
1967         }
1968
1969         if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1970                 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1971                 goto outrun;
1972         }
1973
1974         retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
1975         user->dahdichannel = !retrydahdi;
1976
1977  dahdiretry:
1978         origfd = chan->fds[0];
1979         if (retrydahdi) {
1980                 fd = open("/dev/dahdi/pseudo", O_RDWR);
1981                 if (fd < 0) {
1982                         ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1983                         goto outrun;
1984                 }
1985                 using_pseudo = 1;
1986                 /* Make non-blocking */
1987                 flags = fcntl(fd, F_GETFL);
1988                 if (flags < 0) {
1989                         ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1990                         close(fd);
1991                         goto outrun;
1992                 }
1993                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1994                         ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1995                         close(fd);
1996                         goto outrun;
1997                 }
1998                 /* Setup buffering information */
1999                 memset(&bi, 0, sizeof(bi));
2000                 bi.bufsize = CONF_SIZE / 2;
2001                 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
2002                 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
2003                 bi.numbufs = audio_buffers;
2004                 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
2005                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
2006                         close(fd);
2007                         goto outrun;
2008                 }
2009                 x = 1;
2010                 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
2011                         ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
2012                         close(fd);
2013                         goto outrun;
2014                 }
2015                 nfds = 1;
2016         } else {
2017                 /* XXX Make sure we're not running on a pseudo channel XXX */
2018                 fd = chan->fds[0];
2019                 nfds = 0;
2020         }
2021         memset(&dahdic, 0, sizeof(dahdic));
2022         memset(&dahdic_empty, 0, sizeof(dahdic_empty));
2023         /* Check to see if we're in a conference... */
2024         dahdic.chan = 0;        
2025         if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
2026                 ast_log(LOG_WARNING, "Error getting conference\n");
2027                 close(fd);
2028                 goto outrun;
2029         }
2030         if (dahdic.confmode) {
2031                 /* Whoa, already in a conference...  Retry... */
2032                 if (!retrydahdi) {
2033                         ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
2034                         retrydahdi = 1;
2035                         goto dahdiretry;
2036                 }
2037         }
2038         memset(&dahdic, 0, sizeof(dahdic));
2039         /* Add us to the conference */
2040         dahdic.chan = 0;        
2041         dahdic.confno = conf->dahdiconf;
2042
2043         ast_mutex_lock(&conf->playlock);
2044
2045         if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
2046                 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
2047                         if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2048                                 ast_waitstream(conf->chan, "");
2049                         if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
2050                                 ast_waitstream(conf->chan, "");
2051                 }
2052         }
2053
2054         if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers)
2055                 dahdic.confmode = DAHDI_CONF_CONF;
2056         else if (confflags & CONFFLAG_MONITOR)
2057                 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2058         else if (confflags & CONFFLAG_TALKER)
2059                 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2060         else 
2061                 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2062
2063         if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2064                 ast_log(LOG_WARNING, "Error setting conference\n");
2065                 close(fd);
2066                 ast_mutex_unlock(&conf->playlock);
2067                 goto outrun;
2068         }
2069         ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
2070
2071         if (!sent_event) {
2072                 manager_event(EVENT_FLAG_CALL, "MeetmeJoin", 
2073                                 "Channel: %s\r\n"
2074                                 "Uniqueid: %s\r\n"
2075                                 "Meetme: %s\r\n"
2076                                 "Usernum: %d\r\n"
2077                                 "CallerIDnum: %s\r\n"
2078                                 "CallerIDname: %s\r\n",
2079                                 chan->name, chan->uniqueid, conf->confno, 
2080                                 user->user_no,
2081                                 S_OR(user->chan->cid.cid_num, "<unknown>"),
2082                                 S_OR(user->chan->cid.cid_name, "<unknown>")
2083                                 );
2084                 sent_event = 1;
2085         }
2086
2087         if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
2088                 firstpass = 1;
2089                 if (!(confflags & CONFFLAG_QUIET))
2090                         if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
2091                                 conf_play(chan, conf, ENTER);
2092         }
2093
2094         ast_mutex_unlock(&conf->playlock);
2095
2096         conf_flush(fd, chan);
2097
2098         if (confflags & CONFFLAG_AGI) {
2099                 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
2100                    or use default filename of conf-background.agi */
2101
2102                 ast_channel_lock(chan);
2103                 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
2104                         agifile = ast_strdupa(tmpvar);
2105                 } else {
2106                         agifile = ast_strdupa(agifiledefault);
2107                 }
2108                 ast_channel_unlock(chan);
2109                 
2110                 if (user->dahdichannel) {
2111                         /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
2112                         x = 1;
2113                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2114                 }
2115                 /* Find a pointer to the agi app and execute the script */
2116                 agi_app = pbx_findapp("agi");
2117                 if (agi_app) {
2118                         ret = pbx_exec(chan, agi_app, agifile);
2119                 } else {
2120                         ast_log(LOG_WARNING, "Could not find application (agi)\n");
2121                         ret = -2;
2122                 }
2123                 if (user->dahdichannel) {
2124                         /*  Remove CONFMUTE mode on DAHDI channel */
2125                         x = 0;
2126                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2127                 }
2128         } else {
2129                 if (user->dahdichannel && (confflags & CONFFLAG_STARMENU)) {
2130                         /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
2131                         x = 1;
2132                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2133                 }       
2134                 if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
2135                         ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
2136                         res = -1;
2137                 }
2138                 for (;;) {
2139                         int menu_was_active = 0;
2140
2141                         outfd = -1;
2142                         ms = -1;
2143                         now = ast_tvnow();
2144
2145                         if (rt_schedule && conf->endtime) {
2146                                 char currenttime[32];
2147                                 long localendtime = 0;
2148                                 int extended = 0;
2149                                 struct ast_tm tm;
2150                                 struct ast_variable *var, *origvar;
2151                                 struct timeval tmp;
2152
2153                                 if (now.tv_sec % 60 == 0) {
2154                                         if (!checked) {
2155                                                 ast_localtime(&now, &tm, NULL);
2156                                                 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2157                                                 var = origvar = ast_load_realtime("meetme", "confno",
2158                                                         conf->confno, "starttime <=", currenttime,
2159                                                          "endtime >=", currenttime, NULL);
2160
2161                                                 for ( ; var; var = var->next) {
2162                                                         if (!strcasecmp(var->name, "endtime")) {
2163                                                                 struct ast_tm endtime_tm;
2164                                                                 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
2165                                                                 tmp = ast_mktime(&endtime_tm, NULL);
2166                                                                 localendtime = tmp.tv_sec;
2167                                                         }
2168                                                 }
2169                                                 ast_variables_destroy(origvar);
2170
2171                                                 /* A conference can be extended from the
2172                                                    Admin/User menu or by an external source */
2173                                                 if (localendtime > conf->endtime){
2174                                                         conf->endtime = localendtime;
2175                                                         extended = 1;
2176                                                 }
2177
2178                                                 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
2179                                                         ast_verbose("Quitting time...\n");
2180                                                         goto outrun;
2181                                                 }
2182
2183                                                 if (!announcement_played && conf->endalert) {
2184                                                         if (now.tv_sec + conf->endalert >= conf->endtime) {
2185                                                                 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
2186                                                                         ast_waitstream(chan, "");
2187                                                                 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
2188                                                                 if (!ast_streamfile(chan, "minutes", chan->language))
2189                                                                         ast_waitstream(chan, "");
2190                                                                 announcement_played = 1;
2191                                                         }
2192                                                 }
2193
2194                                                 if (extended) {
2195                                                         announcement_played = 0;
2196                                                 }
2197
2198                                                 checked = 1;
2199                                         }
2200                                 } else {
2201                                         checked = 0;
2202                                 }
2203                         }
2204
2205                         if (user->kicktime && (user->kicktime <= now.tv_sec)) {
2206                                 break;
2207                         }
2208   
2209                         to = -1;
2210                         if (user->timelimit) {
2211                                 int minutes = 0, seconds = 0, remain = 0;
2212  
2213                                 to = ast_tvdiff_ms(nexteventts, now);
2214                                 if (to < 0) {
2215                                         to = 0;
2216                                 }
2217                                 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
2218                                 if (time_left_ms < to) {
2219                                         to = time_left_ms;
2220                                 }
2221         
2222                                 if (time_left_ms <= 0) {
2223                                         if (user->end_sound) {                                          
2224                                                 res = ast_streamfile(chan, user->end_sound, chan->language);
2225                                                 res = ast_waitstream(chan, "");
2226                                         }
2227                                         break;
2228                                 }
2229                                 
2230                                 if (!to) {
2231                                         if (time_left_ms >= 5000) {                                             
2232                                                 
2233                                                 remain = (time_left_ms + 500) / 1000;
2234                                                 if (remain / 60 >= 1) {
2235                                                         minutes = remain / 60;
2236                                                         seconds = remain % 60;
2237                                                 } else {
2238                                                         seconds = remain;
2239                                                 }
2240                                                 
2241                                                 /* force the time left to round up if appropriate */
2242                                                 if (user->warning_sound && user->play_warning) {
2243                                                         if (!strcmp(user->warning_sound, "timeleft")) {
2244                                                                 
2245                                                                 res = ast_streamfile(chan, "vm-youhave", chan->language);
2246                                                                 res = ast_waitstream(chan, "");
2247                                                                 if (minutes) {
2248                                                                         res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2249                                                                         res = ast_streamfile(chan, "queue-minutes", chan->language);
2250                                                                         res = ast_waitstream(chan, "");
2251                                                                 }
2252                                                                 if (seconds) {
2253                                                                         res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2254                                                                         res = ast_streamfile(chan, "queue-seconds", chan->language);
2255                                                                         res = ast_waitstream(chan, "");
2256                                                                 }
2257                                                         } else {
2258                                                                 res = ast_streamfile(chan, user->warning_sound, chan->language);
2259                                                                 res = ast_waitstream(chan, "");
2260                                                         }
2261                                                 }
2262                                         }
2263                                         if (user->warning_freq) {
2264                                                 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2265                                         } else {
2266                                                 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2267                                         }
2268                                 }
2269                         }
2270
2271                         now = ast_tvnow();
2272                         if (timeout && now.tv_sec >= timeout) {
2273                                 break;
2274                         }
2275
2276                         /* if we have just exited from the menu, and the user had a channel-driver
2277                            volume adjustment, restore it
2278                         */
2279                         if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual) {
2280                                 set_talk_volume(user, user->listen.desired);
2281                         }
2282
2283                         menu_was_active = menu_active;
2284
2285                         currentmarked = conf->markedusers;
2286                         if (!(confflags & CONFFLAG_QUIET) &&
2287                             (confflags & CONFFLAG_MARKEDUSER) &&
2288                             (confflags & CONFFLAG_WAITMARKED) &&
2289                             lastmarked == 0) {
2290                                 if (currentmarked == 1 && conf->users > 1) {
2291                                         ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2292                                         if (conf->users - 1 == 1) {
2293                                                 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language)) {
2294                                                         ast_waitstream(chan, "");
2295                                                 }
2296                                         } else {
2297                                                 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language)) {
2298                                                         ast_waitstream(chan, "");
2299                                                 }
2300                                         }
2301                                 }
2302                                 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER)) {
2303                                         if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
2304                                                 ast_waitstream(chan, "");
2305                                         }
2306                                 }
2307                         }
2308
2309                         c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
2310
2311                         /* Update the struct with the actual confflags */
2312                         user->userflags = confflags;
2313
2314                         if (confflags & CONFFLAG_WAITMARKED) {
2315                                 if (currentmarked == 0) {
2316                                         if (lastmarked != 0) {
2317                                                 if (!(confflags & CONFFLAG_QUIET)) {
2318                                                         if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language)) {
2319                                                                 ast_waitstream(chan, "");
2320                                                         }
2321                                                 }
2322                                                 if (confflags & CONFFLAG_MARKEDEXIT) {
2323                                                         if (confflags & CONFFLAG_KICK_CONTINUE) {
2324                                                                 ret = 0;
2325                                                         }
2326                                                         break;
2327                                                 } else {
2328                                                         dahdic.confmode = DAHDI_CONF_CONF;
2329                                                         if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2330                                                                 ast_log(LOG_WARNING, "Error setting conference\n");
2331                                                                 close(fd);
2332                                                                 goto outrun;
2333                                                         }
2334                                                 }
2335                                         }
2336                                         if (!musiconhold && (confflags & CONFFLAG_MOH)) {
2337                                                 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2338                                                 musiconhold = 1;
2339                                         }
2340                                 } else if (currentmarked >= 1 && lastmarked == 0) {
2341                                         /* Marked user entered, so cancel timeout */
2342                                         timeout = 0;
2343                                         if (confflags & CONFFLAG_MONITOR) {
2344                                                 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2345                                         } else if (confflags & CONFFLAG_TALKER) {
2346                                                 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2347                                         } else {
2348                                                 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2349                                         }
2350                                         if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2351                                                 ast_log(LOG_WARNING, "Error setting conference\n");
2352                                                 close(fd);
2353                                                 goto outrun;
2354                                         }
2355                                         if (musiconhold && (confflags & CONFFLAG_MOH)) {
2356                                                 ast_moh_stop(chan);
2357                                                 musiconhold = 0;
2358                                         }
2359                                         if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
2360                                                 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language)) {
2361                                                         ast_waitstream(chan, "");
2362                                                 }
2363                                                 conf_play(chan, conf, ENTER);
2364                                         }
2365                                 }
2366                         }
2367
2368                         /* trying to add moh for single person conf */
2369                         if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
2370                                 if (conf->users == 1) {
2371                                         if (!musiconhold) {
2372                                                 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2373                                                 musiconhold = 1;
2374                                         } 
2375                                 } else {
2376                                         if (musiconhold) {
2377                                                 ast_moh_stop(chan);
2378                                                 musiconhold = 0;
2379                                         }
2380                                 }
2381                         }
2382                         
2383                         /* Leave if the last marked user left */
2384                         if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
2385                                 if (confflags & CONFFLAG_KICK_CONTINUE) {
2386                                         ret = 0;
2387                                 } else {
2388                                         ret = -1;
2389                                 }
2390                                 break;
2391                         }
2392         
2393                         /* Check if my modes have changed */
2394
2395                         /* If I should be muted but am still talker, mute me */
2396                         if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
2397                                 dahdic.confmode ^= DAHDI_CONF_TALKER;
2398                                 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2399                                         ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2400                                         ret = -1;
2401                                         break;
2402                                 }
2403
2404                                 manager_event(EVENT_FLAG_CALL, "MeetmeMute", 
2405                                                 "Channel: %s\r\n"
2406                                                 "Uniqueid: %s\r\n"
2407                                                 "Meetme: %s\r\n"
2408                                                 "Usernum: %i\r\n"
2409                                                 "Status: on\r\n",
2410                                                 chan->name, chan->uniqueid, conf->confno, user->user_no);
2411                         }
2412
2413                         /* If I should be un-muted but am not talker, un-mute me */
2414                         if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
2415                                 dahdic.confmode |= DAHDI_CONF_TALKER;
2416                                 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2417                                         ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2418                                         ret = -1;
2419                                         break;
2420                                 }
2421
2422                                 manager_event(EVENT_FLAG_CALL, "MeetmeMute", 
2423                                                 "Channel: %s\r\n"
2424                                                 "Uniqueid: %s\r\n"
2425                                                 "Meetme: %s\r\n"
2426                                                 "Usernum: %i\r\n"
2427                                                 "Status: off\r\n",
2428                                                 chan->name, chan->uniqueid, conf->confno, user->user_no);
2429                         }
2430                         
2431                         if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
2432                                 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
2433                                 talkreq_manager = 1;
2434
2435                                 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest", 
2436                                               "Channel: %s\r\n"
2437                                                               "Uniqueid: %s\r\n"
2438                                                               "Meetme: %s\r\n"
2439                                                               "Usernum: %i\r\n"
2440                                                               "Status: on\r\n",
2441                                                               chan->name, chan->uniqueid, conf->confno, user->user_no);
2442                         }
2443
2444                         
2445                         if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
2446                                 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
2447                                 talkreq_manager = 0;
2448                                 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest", 
2449                                               "Channel: %s\r\n"
2450                                                               "Uniqueid: %s\r\n"
2451                                                               "Meetme: %s\r\n"
2452                                                               "Usernum: %i\r\n"
2453                                                               "Status: off\r\n",
2454                                                              chan->name, chan->uniqueid, conf->confno, user->user_no);
2455                         }
2456                         
2457                         /* If I have been kicked, exit the conference */
2458                         if (user->adminflags & ADMINFLAG_KICKME) {
2459                                 /* You have been kicked. */
2460                                 if (!(confflags & CONFFLAG_QUIET) && 
2461                                         !ast_streamfile(chan, "conf-kicked", chan->language)) {
2462                                         ast_waitstream(chan, "");
2463                                 }
2464                                 ret = 0;
2465                                 break;
2466                         }
2467
2468                         /* Perform an extra hangup check just in case */
2469                         if (ast_check_hangup(chan)) {
2470                                 break;
2471                         }
2472
2473                         if (c) {
2474                                 char dtmfstr[2] = "";
2475
2476                                 if (c->fds[0] != origfd || (user->dahdichannel && (c->audiohooks || c->monitor))) {
2477                                         if (using_pseudo) {
2478                                                 /* Kill old pseudo */
2479                                                 close(fd);
2480                                                 using_pseudo = 0;
2481                                         }
2482                                         ast_debug(1, "Ooh, something swapped out under us, starting over\n");
2483                                         retrydahdi = (strcasecmp(c->tech->type, "DAHDI") || (c->audiohooks || c->monitor) ? 1 : 0);
2484                                         user->dahdichannel = !retrydahdi;
2485                                         goto dahdiretry;
2486                                 }
2487                                 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2488                                         f = ast_read_noaudio(c);
2489                                 } else {
2490                                         f = ast_read(c);
2491                                 }
2492                                 if (!f) {
2493                                         break;
2494                                 }
2495                                 if (f->frametype == AST_FRAME_DTMF) {
2496                                         dtmfstr[0] = f->subclass;
2497                                         dtmfstr[1] = '\0';
2498                                 }
2499
2500                                 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
2501                                         if (user->talk.actual) {
2502                                                 ast_frame_adjust_volume(f, user->talk.actual);
2503                                         }
2504
2505                                         if (!(confflags & CONFFLAG_MONITOR)) {
2506                                                 int totalsilence;
2507
2508                                                 if (user->talking == -1) {
2509                                                         user->talking = 0;
2510                                                 }
2511
2512                                                 res = ast_dsp_silence(dsp, f, &totalsilence);
2513                                                 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
2514                                                         user->talking = 1;
2515                                                         if (confflags & CONFFLAG_MONITORTALKER)
2516                                                                 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2517                                                                       "Channel: %s\r\n"
2518                                                                       "Uniqueid: %s\r\n"
2519                                                                       "Meetme: %s\r\n"
2520                                                                       "Usernum: %d\r\n"
2521                                                                       "Status: on\r\n",
2522                                                                       chan->name, chan->uniqueid, conf->confno, user->user_no);
2523                                                 }
2524                                                 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
2525                                                         user->talking = 0;
2526                                                         if (confflags & CONFFLAG_MONITORTALKER) {
2527                                                                 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2528                                                                       "Channel: %s\r\n"
2529                                                                       "Uniqueid: %s\r\n"
2530                                                                       "Meetme: %s\r\n"
2531                                                                       "Usernum: %d\r\n"
2532                                                                       "Status: off\r\n",
2533                                                                       chan->name, chan->uniqueid, conf->confno, user->user_no);
2534                                                         }
2535                                                 }
2536                                         }
2537                                         if (using_pseudo) {
2538                                                 /* Absolutely do _not_ use careful_write here...
2539                                                    it is important that we read data from the channel
2540                                                    as fast as it arrives, and feed it into the conference.
2541                                                    The buffering in the pseudo channel will take care of any
2542                                                    timing differences, unless they are so drastic as to lose
2543                                                    audio frames (in which case carefully writing would only
2544                                                    have delayed the audio even further).
2545                                                 */
2546                                                 /* As it turns out, we do want to use careful write.  We just
2547                                                    don't want to block, but we do want to at least *try*
2548                                                    to write out all the samples.
2549                                                  */
2550                                                 if (user->talking) {
2551                                                         careful_write(fd, f->data.ptr, f->datalen, 0);
2552                                                 }
2553                                         }
2554                                 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2555                                         if (confflags & CONFFLAG_PASS_DTMF) {
2556                                                 conf_queue_dtmf(conf, user, f);
2557                                         }
2558                                         if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
2559                                                 ast_log(LOG_WARNING, "Error setting conference\n");
2560                                                 close(fd);
2561                                                 ast_frfree(f);
2562                                                 goto outrun;
2563                                         }
2564
2565                                         /* if we are entering the menu, and the user has a channel-driver
2566                                            volume adjustment, clear it
2567                                         */
2568                                         if (!menu_active && user->talk.desired && !user->talk.actual) {
2569                                                 set_talk_volume(user, 0);
2570                                         }
2571
2572                                         if (musiconhold) {
2573                                                 ast_moh_stop(chan);
2574                                         }
2575                                         if ((confflags & CONFFLAG_ADMIN)) {
2576                                                 /* Admin menu */
2577                                                 if (!menu_active) {
2578                                                         menu_active = 1;
2579                                                         /* Record this sound! */
2580                                                         if (!ast_streamfile(chan, "conf-adminmenu-162", chan->language)) {
2581                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2582                                                                 ast_stopstream(chan);
2583                                                         } else {
2584                                                                 dtmf = 0;
2585                                                         }
2586                                                 } else {
2587                                                         dtmf = f->subclass;
2588                                                 }
2589                                                 if (dtmf) {
2590                                                         switch(dtmf) {
2591                                                         case '1': /* Un/Mute */
2592                                                                 menu_active = 0;
2593
2594                                                                 /* for admin, change both admin and use flags */
2595                                                                 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2596                                                                         user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2597                                                                 } else {
2598                                                                         user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2599                                                                 }
2600
2601                                                                 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2602                                                                         if (!ast_streamfile(chan, "conf-muted", chan->language)) {
2603                                                                                 ast_waitstream(chan, "");
2604                                                                         }
2605                                                                 } else {
2606                                                                         if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
2607                                                                                 ast_waitstream(chan, "");
2608                                                                         }
2609                                                                 }
2610                                                                 break;
2611                                                         case '2': /* Un/Lock the Conference */
2612                                                                 menu_active = 0;
2613                                                                 if (conf->locked) {
2614                                                                         conf->locked = 0;
2615                                                                         if (!ast_streamfile(chan, "conf-unlockednow", chan->language)) {
2616                                                                                 ast_waitstream(chan, "");
2617                                                                         }
2618                                                                 } else {
2619                                                                         conf->locked = 1;
2620                                                                         if (!ast_streamfile(chan, "conf-lockednow", chan->language)) {
2621                                                                                 ast_waitstream(chan, "");
2622                                                                         }
2623                                                                 }
2624                                                                 break;
2625                                                         case '3': /* Eject last user */
2626                                                                 menu_active = 0;
2627                                                                 usr = AST_LIST_LAST(&conf->userlist);
2628                                                                 if ((usr->chan->name == chan->name) || (usr->userflags & CONFFLAG_ADMIN)) {
2629                                                                         if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2630                                                                                 ast_waitstream(chan, "");
2631                                                                         }
2632                                                                 } else {
2633                                                                         usr->adminflags |= ADMINFLAG_KICKME;
2634                                                                 }
2635                                                                 ast_stopstream(chan);
2636                                                                 break;  
2637                                                         case '4':
2638                                                                 tweak_listen_volume(user, VOL_DOWN);
2639                                                                 break;
2640                                                         case '5':
2641                                                                 /* Extend RT conference */
2642                                                                 if (rt_schedule) {
2643                                                                         if (!rt_extend_conf(conf->confno)) {
2644                                                                                 if (!ast_streamfile(chan, "conf-extended", chan->language)) {
2645                                                                                         ast_waitstream(chan, "");
2646                                                                                 }
2647                                                                         } else {
2648                                                                                 if (!ast_streamfile(chan, "conf-nonextended", chan->language)) {
2649                                                                                         ast_waitstream(chan, "");
2650                                                                                 }
2651                                                                         }
2652                                                                         ast_stopstream(chan);
2653                                                                 }
2654                                                                 menu_active = 0;
2655                                                                 break;
2656                                                         case '6':
2657                                                                 tweak_listen_volume(user, VOL_UP);
2658                                                                 break;
2659                                                         case '7':
2660                                                                 tweak_talk_volume(user, VOL_DOWN);
2661                                                                 break;
2662                                                         case '8':
2663                                                                 menu_active = 0;
2664                                                                 break;
2665                                                         case '9':
2666                                                                 tweak_talk_volume(user, VOL_UP);
2667                                                                 break;
2668                                                         default:
2669                                                                 menu_active = 0;
2670                                                                 /* Play an error message! */
2671                                                                 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2672                                                                         ast_waitstream(chan, "");
2673                                                                 }
2674                                                                 break;
2675                                                         }
2676                                                 }
2677                                         } else {
2678                                                 /* User menu */
2679                                                 if (!menu_active) {
2680                                                         menu_active = 1;
2681                                                         if (!ast_streamfile(chan, "conf-usermenu-162", chan->language)) {
2682                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2683                                                                 ast_stopstream(chan);
2684                                                         } else {
2685                                                                 dtmf = 0;
2686                                                         }
2687                                                 } else {
2688                                                         dtmf = f->subclass;
2689                                                 }
2690                                                 if (dtmf) {
2691                                                         switch (dtmf) {
2692                                                         case '1': /* Un/Mute */
2693                                                                 menu_active = 0;
2694
2695                                                                 /* user can only toggle the self-muted state */
2696                                                                 user->adminflags ^= ADMINFLAG_SELFMUTED;
2697
2698                                                                 /* they can't override the admin mute state */
2699                                                                 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2700                                                                         if (!ast_streamfile(chan, "conf-muted", chan->language)) {
2701                                                                                 ast_waitstream(chan, "");
2702                                                                         }
2703                                                                 } else {
2704                                                                         if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
2705                                                                                 ast_waitstream(chan, "");
2706                                                                         }
2707                                                                 }
2708                                                                 break;
2709                                                         case '2':
2710                                                                 menu_active = 0;
2711                                                                 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2712                                                                         user->adminflags |= ADMINFLAG_T_REQUEST;
2713                                                                 }
2714                                                                         
2715                                                                 if (user->adminflags & ADMINFLAG_T_REQUEST) {
2716                                                                         if (!ast_streamfile(chan, "beep", chan->language)) {
2717                                                                                 ast_waitstream(chan, "");
2718                                                                         }
2719                                                                 }
2720                                                                 break;
2721                                                         case '4':
2722                                                                 tweak_listen_volume(user, VOL_DOWN);
2723                                                                 break;
2724                                                         case '5':
2725                                                                 /* Extend RT conference */
2726                                                                 if (rt_schedule) {
2727                                                                         rt_extend_conf(conf->confno);
2728                                                                 }
2729                                                                 menu_active = 0;
2730                                                                 break;
2731                                                         case '6':
2732                                                                 tweak_listen_volume(user, VOL_UP);
2733                                                                 break;
2734                                                         case '7':
2735                                                                 tweak_talk_volume(user, VOL_DOWN);
2736                                                                 break;
2737                                                         case '8':
2738                                                                 menu_active = 0;
2739                                                                 break;
2740                                                         case '9':
2741                                                                 tweak_talk_volume(user, VOL_UP);
2742                                                                 break;
2743                                                         default:
2744                                                                 menu_active = 0;
2745                                                                 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2746                                                                         ast_waitstream(chan, "");
2747                                                                 }
2748                                                                 break;
2749                                                         }
2750                                                 }
2751                                         }
2752                                         if (musiconhold) {
2753                                                 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2754                                         }
2755
2756                                         if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2757                                                 ast_log(LOG_WARNING, "Error setting conference\n");
2758                                                 close(fd);
2759                                                 ast_frfree(f);
2760                                                 goto outrun;
2761                                         }
2762
2763                                         conf_flush(fd, chan);
2764                                 /* Since this option could absorb DTMF meant for the previous (menu), we have to check this one last */
2765                                 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
2766                                         if (confflags & CONFFLAG_PASS_DTMF) {
2767                                                 conf_queue_dtmf(conf, user, f);
2768                                         }
2769
2770                                         if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
2771                                                 ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
2772                                                 ret = 0;
2773                                                 ast_frfree(f);
2774                                                 break;
2775                                         } else {
2776                                                 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
2777                                         }
2778                                 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
2779                                         pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
2780                                                 
2781                                         if (confflags & CONFFLAG_PASS_DTMF) {
2782                                                 conf_queue_dtmf(conf, user, f);
2783                                         }
2784                                         ret = 0;
2785                                         ast_frfree(f);
2786                                         break;
2787                                 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2788                                         && confflags & CONFFLAG_PASS_DTMF) {
2789                                         conf_queue_dtmf(conf, user, f);
2790                                 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2791                                         switch (f->subclass) {
2792                                         case AST_CONTROL_HOLD:
2793                                                 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2794                                                 break;
2795                                         default:
2796                                                 break;
2797                                         }
2798                                 } else if (f->frametype == AST_FRAME_NULL) {
2799                                         /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2800                                 } else {
2801                                         ast_debug(1, 
2802                                                 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2803                                                 chan->name, f->frametype, f->subclass);
2804                                 }
2805                                 ast_frfree(f);
2806                         } else if (outfd > -1) {
2807                                 res = read(outfd, buf, CONF_SIZE);
2808                                 if (res > 0) {
2809                                         memset(&fr, 0, sizeof(fr));
2810                                         fr.frametype = AST_FRAME_VOICE;
2811                                         fr.subclass = AST_FORMAT_SLINEAR;
2812                                         fr.datalen = res;
2813                                         fr.samples = res / 2;
2814                                         fr.data.ptr = buf;
2815                                         fr.offset = AST_FRIENDLY_OFFSET;
2816                                         if (!user->listen.actual && 
2817                                                 ((confflags & CONFFLAG_MONITOR) || 
2818                                                  (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2819                                                  (!user->talking)) ) {
2820                                                 int idx;
2821                                                 for (idx = 0; idx < AST_FRAME_BITS; idx++) {
2822                                                         if (chan->rawwriteformat & (1 << idx)) {
2823                                                                 break;
2824                                                         }
2825                                                 }
2826                                                 if (idx >= AST_FRAME_BITS) {
2827                                                         goto bailoutandtrynormal;
2828                                                 }
2829                                                 ast_mutex_lock(&conf->listenlock);
2830                                                 if (!conf->transframe[idx]) {
2831                                                         if (conf->origframe) {
2832                                                                 if (!conf->transpath[idx]) {
2833                                                                         conf->transpath[idx] = ast_translator_build_path((1 << idx), AST_FORMAT_SLINEAR);
2834                                                                 }
2835                                                                 if (conf->transpath[idx]) {
2836                                                                         conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
2837                                                                         if (!conf->transframe[idx]) {
2838                                                                                 conf->transframe[idx] = &ast_null_frame;
2839                                                                         }
2840                                                                 }
2841                                                         }
2842                                                 }
2843                                                 if (conf->transframe[idx]) {
2844                                                         if (conf->transframe[idx]->frametype != AST_FRAME_NULL) {
2845                                                                 if (ast_write(chan, conf->transframe[idx])) {
2846                                                                         ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2847                                                                 }
2848                                                         }
2849                                                 } else {
2850                                                         ast_mutex_unlock(&conf->listenlock);
2851                                                         goto bailoutandtrynormal;
2852                                                 }
2853                                                 ast_mutex_unlock(&conf->listenlock);
2854                                         } else {
2855 bailoutandtrynormal:                                    
2856                                                 if (user->listen.actual) {
2857                                                         ast_frame_adjust_volume(&fr, user->listen.actual);
2858                                                 }
2859                                                 if (ast_write(chan, &fr) < 0) {
2860                                                         ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2861                                                 }
2862                                         }
2863                                 } else {
2864                                         ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2865                                 }
2866                         }
2867                         lastmarked = currentmarked;
2868                 }
2869         }
2870
2871         if (musiconhold) {
2872                 ast_moh_stop(chan);
2873         }
2874         
2875         if (using_pseudo) {
2876                 close(fd);
2877         } else {
2878                 /* Take out of conference */
2879                 dahdic.chan = 0;        
2880                 dahdic.confno = 0;
2881                 dahdic.confmode = 0;
2882                 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2883                         ast_log(LOG_WARNING, "Error setting conference\n");
2884                 }
2885         }
2886
2887         reset_volumes(user);
2888
2889         AST_LIST_LOCK(&confs);
2890         if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
2891                 conf_play(chan, conf, LEAVE);
2892         }
2893
2894         if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2895                 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2896                         if ((conf->chan) && (conf->users > 1)) {
2897                                 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language)) {
2898                                         ast_waitstream(conf->chan, "");
2899                                 }
2900                                 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language)) {
2901                                         ast_waitstream(conf->chan, "");
2902                                 }
2903                         }
2904                         ast_filedelete(user->namerecloc, NULL);
2905                 }
2906         }
2907         AST_LIST_UNLOCK(&confs);
2908
2909  outrun:
2910         AST_LIST_LOCK(&confs);
2911
2912         if (dsp) {
2913                 ast_dsp_free(dsp);
2914         }
2915         
2916         if (user->user_no) { /* Only cleanup users who really joined! */
2917                 now = ast_tvnow();
2918                 hr = (now.tv_sec - user->jointime) / 3600;
2919                 min = ((now.tv_sec - user->jointime) % 3600) / 60;
2920                 sec = (now.tv_sec - user->jointime) % 60;
2921
2922                 if (sent_event) {
2923                         manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2924                                       "Channel: %s\r\n"
2925                                       "Uniqueid: %s\r\n"
2926                                       "Meetme: %s\r\n"
2927                                       "Usernum: %d\r\n"
2928                                       "CallerIDNum: %s\r\n"
2929                                       "CallerIDName: %s\r\n"
2930                                       "Duration: %ld\r\n",
2931                                       chan->name, chan->uniqueid, conf->confno, 
2932                                       user->user_no,
2933                                       S_OR(user->chan->cid.cid_num, "<unknown>"),
2934                                       S_OR(user->chan->cid.cid_name, "<unknown>"),
2935                                       (long)(now.tv_sec - user->jointime));
2936                 }
2937
2938                 if (setusercount) {
2939                         conf->users--;
2940                         if (rt_log_members) {
2941                                 /* Update table */
2942                                 snprintf(members, sizeof(members), "%d", conf->users);
2943                                 ast_realtime_require_field("meetme",
2944                                         "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
2945                                         "members", RQ_UINTEGER1, strlen(members),
2946                                         NULL);
2947                                 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2948                         }
2949                         if (confflags & CONFFLAG_MARKEDUSER) {
2950                                 conf->markedusers--;
2951                         }
2952                 }
2953                 /* Remove ourselves from the list */
2954                 AST_LIST_REMOVE(&conf->userlist, user, list);
2955
2956                 /* Change any states */
2957                 if (!conf->users) {
2958                         ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
2959                 }
2960
2961                 /* Return the number of seconds the user was in the conf */
2962                 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2963                 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2964         }
2965         ast_free(user);
2966         AST_LIST_UNLOCK(&confs);
2967
2968         return ret;
2969 }
2970
2971 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2972                                 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags, int *too_early)
2973 {
2974         struct ast_variable *var, *origvar;
2975         struct ast_conference *cnf;
2976
2977         *too_early = 0;
2978
2979         /* Check first in the conference list */
2980         AST_LIST_LOCK(&confs);
2981         AST_LIST_TRAVERSE(&confs, cnf, list) {
2982                 if (!strcmp(confno, cnf->confno)) 
2983                         break;
2984         }
2985         if (cnf) {
2986                 cnf->refcount += refcount;
2987         }
2988         AST_LIST_UNLOCK(&confs);
2989
2990         if (!cnf) {
2991                 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2992                 int maxusers = 0;
2993                 struct timeval now;
2994                 char currenttime[19] = "";
2995                 char eatime[19] = "";
2996                 char bookid[19] = "";
2997                 char recordingtmp[AST_MAX_EXTENSION] = "";
2998                 char useropts[OPTIONS_LEN]; /* Used for RealTime conferences */
2999                 char adminopts[OPTIONS_LEN];
3000                 struct ast_tm tm, etm;
3001                 struct timeval endtime = { .tv_sec = 0 };
3002
3003                 if (rt_schedule) {
3004                         now = ast_tvnow();
3005
3006                         ast_localtime(&now, &tm, NULL);
3007                         ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
3008
3009                         ast_debug(1, "Looking for conference %s that starts after %s\n", confno, eatime);
3010
3011                         var = ast_load_realtime("meetme", "confno",
3012                                 confno, "starttime <= ", currenttime, "endtime >= ",
3013                                 currenttime, NULL);
3014
3015                         if (!var && fuzzystart) {
3016                                 now = ast_tvnow();
3017                                 now.tv_sec += fuzzystart;
3018
3019                                 ast_localtime(&now, &tm, NULL);
3020                                 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
3021                                 var = ast_load_realtime("meetme", "confno",
3022                                         confno, "starttime <= ", currenttime, "endtime >= ",
3023                                         currenttime, NULL);
3024                         }
3025
3026                         if (!var && earlyalert) {
3027                                 now = ast_tvnow();
3028                                 now.tv_sec += earlyalert;
3029                                 ast_localtime(&now, &etm, NULL);
3030                                 ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
3031                                 var = ast_load_realtime("meetme", "confno",
3032                                         confno, "starttime <= ", eatime, "endtime >= ",
3033                                         currenttime, NULL);
3034                                 if (var) {
3035                                         *too_early = 1;
3036                                 }
3037           &n