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