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