b7c4cc6f2976c879d4dbc3e931c681244087f9b5
[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 (sla.thread == AST_PTHREADT_NULL) {
1504                 return;
1505         }
1506
1507         if (!(event = ast_calloc(1, sizeof(*event))))
1508                 return;
1509
1510         event->type = type;
1511         event->trunk_ref = trunk_ref;
1512         event->station = station;
1513
1514         if (!lock) {
1515                 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1516                 return;
1517         }
1518
1519         ast_mutex_lock(&sla.lock);
1520         AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1521         ast_cond_signal(&sla.cond);
1522         ast_mutex_unlock(&sla.lock);
1523 }
1524
1525 static void sla_queue_event_nolock(enum sla_event_type type)
1526 {
1527         sla_queue_event_full(type, NULL, NULL, 0);
1528 }
1529
1530 static void sla_queue_event(enum sla_event_type type)
1531 {
1532         sla_queue_event_full(type, NULL, NULL, 1);
1533 }
1534
1535 /*! \brief Queue a SLA event from the conference */
1536 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1537         struct ast_conference *conf)
1538 {
1539         struct sla_station *station;
1540         struct sla_trunk_ref *trunk_ref = NULL;
1541         char *trunk_name;
1542
1543         trunk_name = ast_strdupa(conf->confno);
1544         strsep(&trunk_name, "_");
1545         if (ast_strlen_zero(trunk_name)) {
1546                 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1547                 return;
1548         }
1549
1550         AST_RWLIST_RDLOCK(&sla_stations);
1551         AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1552                 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1553                         if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1554                                 break;
1555                 }
1556                 if (trunk_ref)
1557                         break;
1558         }
1559         AST_RWLIST_UNLOCK(&sla_stations);
1560
1561         if (!trunk_ref) {
1562                 ast_debug(1, "Trunk not found for event!\n");
1563                 return;
1564         }
1565
1566         sla_queue_event_full(type, trunk_ref, station, 1);
1567 }
1568
1569 /* Decrement reference counts, as incremented by find_conf() */
1570 static int dispose_conf(struct ast_conference *conf)
1571 {
1572         int res = 0;
1573         int confno_int = 0;
1574
1575         AST_LIST_LOCK(&confs);
1576         if (ast_atomic_dec_and_test(&conf->refcount)) {
1577                 /* Take the conference room number out of an inuse state */
1578                 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
1579                         conf_map[confno_int] = 0;
1580                 }
1581                 conf_free(conf);
1582                 res = 1;
1583         }
1584         AST_LIST_UNLOCK(&confs);
1585
1586         return res;
1587 }
1588
1589 static int rt_extend_conf(char *confno)
1590 {
1591         char currenttime[32];
1592         char endtime[32];
1593         struct timeval now;
1594         struct ast_tm tm;
1595         struct ast_variable *var;
1596         char bookid[8]; 
1597
1598         if (!extendby) {
1599                 return 0;
1600         }
1601
1602         now = ast_tvnow();
1603
1604         ast_localtime(&now, &tm, NULL);
1605         ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1606
1607         var = ast_load_realtime("meetme", "confno",
1608                 confno, "startTime<= ", currenttime,
1609                 "endtime>= ", currenttime, NULL);
1610
1611         /* Identify the specific RealTime conference */
1612         while (var) {
1613                 if (!strcasecmp(var->name, "bookid")) {
1614                         ast_copy_string(bookid, var->value, sizeof(bookid));
1615                 }
1616                 if (!strcasecmp(var->name, "endtime")) {
1617                         ast_copy_string(endtime, var->value, sizeof(endtime));
1618                 }
1619
1620                 var = var->next;
1621         }
1622         ast_variables_destroy(var);
1623
1624         ast_strptime(endtime, DATE_FORMAT, &tm);
1625         now = ast_mktime(&tm, NULL);
1626
1627         now.tv_sec += extendby;
1628
1629         ast_localtime(&now, &tm, NULL);
1630         ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
1631         strcat(currenttime, "0"); /* Seconds needs to be 00 */
1632
1633         var = ast_load_realtime("meetme", "confno",
1634                 confno, "startTime<= ", currenttime,
1635                 "endtime>= ", currenttime, NULL);
1636
1637         /* If there is no conflict with extending the conference, update the DB */
1638         if (!var) {
1639                 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
1640                 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
1641                 return 0;
1642
1643         }
1644
1645         ast_variables_destroy(var);
1646         return -1;
1647 }
1648
1649 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
1650 {
1651         char *original_moh;
1652
1653         ast_channel_lock(chan);
1654         original_moh = ast_strdupa(chan->musicclass);
1655         ast_string_field_set(chan, musicclass, musicclass);
1656         ast_channel_unlock(chan);
1657
1658         ast_moh_start(chan, original_moh, NULL);
1659
1660         ast_channel_lock(chan);
1661         ast_string_field_set(chan, musicclass, original_moh);
1662         ast_channel_unlock(chan);
1663 }
1664
1665 static const char *get_announce_filename(enum announcetypes type)
1666 {
1667         switch (type) {
1668         case CONF_HASLEFT:
1669                 return "conf-hasleft";
1670                 break;
1671         case CONF_HASJOIN:
1672                 return "conf-hasjoin";
1673                 break;
1674         default:
1675                 return "";
1676         }
1677 }
1678
1679 static void *announce_thread(void *data)
1680 {
1681         struct announce_listitem *current;
1682         struct ast_conference *conf = data;
1683         int res;
1684         char filename[PATH_MAX] = "";
1685         AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
1686         AST_LIST_HEAD_INIT_NOLOCK(&local_list);
1687
1688         while (!conf->announcethread_stop) {
1689                 ast_mutex_lock(&conf->announcelistlock);
1690                 if (conf->announcethread_stop) {
1691                         ast_mutex_unlock(&conf->announcelistlock);
1692                         break;
1693                 }
1694                 if (AST_LIST_EMPTY(&conf->announcelist))
1695                         ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
1696
1697                 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
1698                 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
1699
1700                 ast_mutex_unlock(&conf->announcelistlock);
1701                 if (conf->announcethread_stop) {
1702                         break;
1703                 }
1704
1705                 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
1706                         ast_log(LOG_DEBUG, "About to play %s\n", current->namerecloc);
1707                         if (!ast_fileexists(current->namerecloc, NULL, NULL))
1708                                 continue;
1709                         if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
1710                                 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
1711                                         res = ast_waitstream(current->confchan, "");
1712                                 if (!res) {
1713                                         ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
1714                                         if (!ast_streamfile(current->confchan, filename, current->language))
1715                                                 ast_waitstream(current->confchan, "");
1716                                 }
1717                         }
1718                         if (current->announcetype == CONF_HASLEFT) {
1719                                 ast_filedelete(current->namerecloc, NULL);
1720                         }
1721                 }
1722         }
1723
1724         /* thread marked to stop, clean up */
1725         while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
1726                 ast_filedelete(current->namerecloc, NULL);
1727                 ao2_ref(current, -1);
1728         }
1729         return NULL;
1730 }
1731
1732 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1733 {
1734         struct ast_conf_user *user = NULL;
1735         struct ast_conf_user *usr = NULL;
1736         int fd;
1737         struct dahdi_confinfo dahdic, dahdic_empty;
1738         struct ast_frame *f;
1739         struct ast_channel *c;
1740         struct ast_frame fr;
1741         int outfd;
1742         int ms;
1743         int nfds;
1744         int res;
1745         int flags;
1746         int retrydahdi;
1747         int origfd;
1748         int musiconhold = 0;
1749         int firstpass = 0;
1750         int lastmarked = 0;
1751         int currentmarked = 0;
1752         int ret = -1;
1753         int x;
1754         int menu_active = 0;
1755         int talkreq_manager = 0;
1756         int using_pseudo = 0;
1757         int duration = 20;
1758         int hr, min, sec;
1759         int sent_event = 0;
1760         int checked = 0;
1761         int announcement_played = 0;
1762         struct timeval now;
1763         struct ast_dsp *dsp = NULL;
1764         struct ast_app *agi_app;
1765         char *agifile;
1766         const char *agifiledefault = "conf-background.agi", *tmpvar;
1767         char meetmesecs[30] = "";
1768         char exitcontext[AST_MAX_CONTEXT] = "";
1769         char recordingtmp[AST_MAX_EXTENSION] = "";
1770         char members[10] = "";
1771         int dtmf, opt_waitmarked_timeout = 0;
1772         time_t timeout = 0;
1773         struct dahdi_bufferinfo bi;
1774         char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1775         char *buf = __buf + AST_FRIENDLY_OFFSET;
1776         char *exitkeys = NULL;
1777         unsigned int calldurationlimit = 0;
1778         long timelimit = 0;
1779         long play_warning = 0;
1780         long warning_freq = 0;
1781         const char *warning_sound = NULL;
1782         const char *end_sound = NULL;
1783         char *parse;    
1784         long time_left_ms = 0;
1785         struct timeval nexteventts = { 0, };
1786         int to;
1787         int setusercount = 0;
1788
1789         if (!(user = ast_calloc(1, sizeof(*user))))
1790                 return ret;
1791
1792         /* Possible timeout waiting for marked user */
1793         if ((confflags & CONFFLAG_WAITMARKED) &&
1794                 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1795                 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1796                 (opt_waitmarked_timeout > 0)) {
1797                 timeout = time(NULL) + opt_waitmarked_timeout;
1798         }
1799                 
1800         if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
1801                 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
1802                 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
1803         }
1804         
1805         if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
1806                 char *limit_str, *warning_str, *warnfreq_str;
1807                 const char *var;
1808  
1809                 parse = optargs[OPT_ARG_DURATION_LIMIT];
1810                 limit_str = strsep(&parse, ":");
1811                 warning_str = strsep(&parse, ":");
1812                 warnfreq_str = parse;
1813  
1814                 timelimit = atol(limit_str);
1815                 if (warning_str)
1816                         play_warning = atol(warning_str);
1817                 if (warnfreq_str)
1818                         warning_freq = atol(warnfreq_str);
1819  
1820                 if (!timelimit) {
1821                         timelimit = play_warning = warning_freq = 0;
1822                         warning_sound = NULL;
1823                 } else if (play_warning > timelimit) {                  
1824                         if (!warning_freq) {
1825                                 play_warning = 0;
1826                         } else {
1827                                 while (play_warning > timelimit)
1828                                         play_warning -= warning_freq;
1829                                 if (play_warning < 1)
1830                                         play_warning = warning_freq = 0;
1831                         }
1832                 }
1833                 
1834                 ast_channel_lock(chan);
1835                 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
1836                         var = ast_strdupa(var);
1837                 }
1838                 ast_channel_unlock(chan);
1839
1840                 warning_sound = var ? var : "timeleft";
1841                 
1842                 ast_channel_lock(chan);
1843                 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
1844                         var = ast_strdupa(var);
1845                 }
1846                 ast_channel_unlock(chan);
1847                 
1848                 end_sound = var ? var : NULL;
1849                         
1850                 /* undo effect of S(x) in case they are both used */
1851                 calldurationlimit = 0;
1852                 /* more efficient do it like S(x) does since no advanced opts */
1853                 if (!play_warning && !end_sound && timelimit) { 
1854                         calldurationlimit = timelimit / 1000;
1855                         timelimit = play_warning = warning_freq = 0;
1856                 } else {
1857                         ast_debug(2, "Limit Data for this call:\n");
1858                         ast_debug(2, "- timelimit     = %ld\n", timelimit);
1859                         ast_debug(2, "- play_warning  = %ld\n", play_warning);
1860                         ast_debug(2, "- warning_freq  = %ld\n", warning_freq);
1861                         ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
1862                         ast_debug(2, "- end_sound     = %s\n", end_sound ? end_sound : "UNDEF");
1863                 }
1864         }
1865
1866         /* Get exit keys */
1867         if ((confflags & CONFFLAG_KEYEXIT)) {
1868                 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
1869                         exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
1870                 else
1871                         exitkeys = ast_strdupa("#"); /* Default */
1872         }
1873         
1874         if (confflags & CONFFLAG_RECORDCONF) {
1875                 if (!conf->recordingfilename) {
1876                         const char *var;
1877                         ast_channel_lock(chan);
1878                         if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
1879                                 conf->recordingfilename = ast_strdup(var);
1880                         }
1881                         if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
1882                                 conf->recordingformat = ast_strdup(var);
1883                         }
1884                         ast_channel_unlock(chan);
1885                         if (!conf->recordingfilename) {
1886                                 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1887                                 conf->recordingfilename = ast_strdup(recordingtmp);
1888                         }
1889                         if (!conf->recordingformat) {
1890                                 conf->recordingformat = ast_strdup("wav");
1891                         }
1892                         ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1893                                     conf->confno, conf->recordingfilename, conf->recordingformat);
1894                 }
1895         }
1896
1897         ast_mutex_lock(&conf->recordthreadlock);
1898         if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1899                 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1900                 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1901                 dahdic.chan = 0;
1902                 dahdic.confno = conf->dahdiconf;
1903                 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1904                 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
1905                         ast_log(LOG_WARNING, "Error starting listen channel\n");
1906                         ast_hangup(conf->lchan);
1907                         conf->lchan = NULL;
1908                 } else {
1909                         ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
1910                 }
1911         }
1912         ast_mutex_unlock(&conf->recordthreadlock);
1913
1914         ast_mutex_lock(&conf->announcethreadlock);
1915         if ((conf->announcethread == AST_PTHREADT_NULL) && !(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1916                 ast_mutex_init(&conf->announcelistlock);
1917                 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
1918                 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
1919         }
1920         ast_mutex_unlock(&conf->announcethreadlock);
1921
1922         time(&user->jointime);
1923         
1924         user->timelimit = timelimit;
1925         user->play_warning = play_warning;
1926         user->warning_freq = warning_freq;
1927         user->warning_sound = warning_sound;
1928         user->end_sound = end_sound;    
1929         
1930         if (calldurationlimit > 0) {
1931                 time(&user->kicktime);
1932                 user->kicktime = user->kicktime + calldurationlimit;
1933         }
1934         
1935         if (ast_tvzero(user->start_time))
1936                 user->start_time = ast_tvnow();
1937         time_left_ms = user->timelimit;
1938         
1939         if (user->timelimit) {
1940                 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
1941                 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
1942         }
1943
1944         if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1945                 /* Sorry, but this conference is locked! */     
1946                 if (!ast_streamfile(chan, "conf-locked", chan->language))
1947                         ast_waitstream(chan, "");
1948                 goto outrun;
1949         }
1950
1951         ast_mutex_lock(&conf->playlock);
1952
1953         if (AST_LIST_EMPTY(&conf->userlist))
1954                 user->user_no = 1;
1955         else
1956                 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1957
1958         if (rt_schedule && conf->maxusers)
1959                 if (user->user_no > conf->maxusers) {
1960                         /* Sorry, but this confernce has reached the participant limit! */      
1961                         if (!ast_streamfile(chan, "conf-full", chan->language))
1962                                 ast_waitstream(chan, "");
1963                         goto outrun;
1964                 }
1965
1966         AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1967
1968         user->chan = chan;
1969         user->userflags = confflags;
1970         user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1971         user->talking = -1;
1972
1973         ast_mutex_unlock(&conf->playlock);
1974
1975         if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1976                 char destdir[PATH_MAX];
1977
1978                 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
1979
1980                 if (ast_mkdir(destdir, 0777) != 0) {
1981                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
1982                         goto outrun;
1983                 }
1984
1985                 snprintf(user->namerecloc, sizeof(user->namerecloc),
1986                          "%s/meetme-username-%s-%d", destdir,
1987                          conf->confno, user->user_no);
1988                 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1989                         res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
1990                 else
1991                         res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1992                 if (res == -1)
1993                         goto outrun;
1994         }
1995
1996         ast_mutex_lock(&conf->playlock);
1997
1998         if (confflags & CONFFLAG_MARKEDUSER)
1999                 conf->markedusers++;
2000         conf->users++;
2001         if (rt_log_members) {
2002                 /* Update table */
2003                 snprintf(members, sizeof(members), "%d", conf->users);
2004                 ast_realtime_require_field("meetme",
2005                         "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
2006                         "members", RQ_UINTEGER1, strlen(members),
2007                         NULL);
2008                 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2009         }
2010         setusercount = 1;
2011
2012         /* This device changed state now - if this is the first user */
2013         if (conf->users == 1)
2014                 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
2015
2016         ast_mutex_unlock(&conf->playlock);
2017
2018         /* return the unique ID of the conference */
2019         pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
2020
2021         if (confflags & CONFFLAG_EXIT_CONTEXT) {
2022                 ast_channel_lock(chan);
2023                 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
2024                         ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
2025                 } else if (!ast_strlen_zero(chan->macrocontext)) {
2026                         ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
2027                 } else {
2028                         ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
2029                 }
2030                 ast_channel_unlock(chan);
2031         }
2032
2033         if (!(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
2034                 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
2035                         if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2036                                 ast_waitstream(chan, "");
2037                 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
2038                         if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
2039                                 ast_waitstream(chan, "");
2040         }
2041
2042         if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
2043                 int keepplaying = 1;
2044
2045                 if (conf->users == 2) { 
2046                         if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
2047                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2048                                 ast_stopstream(chan);
2049                                 if (res > 0)
2050                                         keepplaying = 0;
2051                                 else if (res == -1)
2052                                         goto outrun;
2053                         }
2054                 } else { 
2055                         if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
2056                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2057                                 ast_stopstream(chan);
2058                                 if (res > 0)
2059                                         keepplaying = 0;
2060                                 else if (res == -1)
2061                                         goto outrun;
2062                         }
2063                         if (keepplaying) {
2064                                 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2065                                 if (res > 0)
2066                                         keepplaying = 0;
2067                                 else if (res == -1)
2068                                         goto outrun;
2069                         }
2070                         if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
2071                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2072                                 ast_stopstream(chan);
2073                                 if (res > 0)
2074                                         keepplaying = 0;
2075                                 else if (res == -1) 
2076                                         goto outrun;
2077                         }
2078                 }
2079         }
2080
2081         ast_indicate(chan, -1);
2082
2083         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
2084                 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
2085                 goto outrun;
2086         }
2087
2088         if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
2089                 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
2090                 goto outrun;
2091         }
2092
2093         retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
2094         user->dahdichannel = !retrydahdi;
2095
2096  dahdiretry:
2097         origfd = chan->fds[0];
2098         if (retrydahdi) {
2099                 fd = open("/dev/dahdi/pseudo", O_RDWR);
2100                 if (fd < 0) {
2101                         ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
2102                         goto outrun;
2103                 }
2104                 using_pseudo = 1;
2105                 /* Make non-blocking */
2106                 flags = fcntl(fd, F_GETFL);
2107                 if (flags < 0) {
2108                         ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
2109                         close(fd);
2110                         goto outrun;
2111                 }
2112                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
2113                         ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
2114                         close(fd);
2115                         goto outrun;
2116                 }
2117                 /* Setup buffering information */
2118                 memset(&bi, 0, sizeof(bi));
2119                 bi.bufsize = CONF_SIZE / 2;
2120                 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
2121                 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
2122                 bi.numbufs = audio_buffers;
2123                 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
2124                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
2125                         close(fd);
2126                         goto outrun;
2127                 }
2128                 x = 1;
2129                 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
2130                         ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
2131                         close(fd);
2132                         goto outrun;
2133                 }
2134                 nfds = 1;
2135         } else {
2136                 /* XXX Make sure we're not running on a pseudo channel XXX */
2137                 fd = chan->fds[0];
2138                 nfds = 0;
2139         }
2140         memset(&dahdic, 0, sizeof(dahdic));
2141         memset(&dahdic_empty, 0, sizeof(dahdic_empty));
2142         /* Check to see if we're in a conference... */
2143         dahdic.chan = 0;        
2144         if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
2145                 ast_log(LOG_WARNING, "Error getting conference\n");
2146                 close(fd);
2147                 goto outrun;
2148         }
2149         if (dahdic.confmode) {
2150                 /* Whoa, already in a conference...  Retry... */
2151                 if (!retrydahdi) {
2152                         ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
2153                         retrydahdi = 1;
2154                         goto dahdiretry;
2155                 }
2156         }
2157         memset(&dahdic, 0, sizeof(dahdic));
2158         /* Add us to the conference */
2159         dahdic.chan = 0;        
2160         dahdic.confno = conf->dahdiconf;
2161
2162         if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
2163                 struct announce_listitem *item;
2164                 if (!(item = ao2_alloc(sizeof(*item), NULL)))
2165                         return -1;
2166                 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
2167                 ast_copy_string(item->language, chan->language, sizeof(item->language));
2168                 item->confchan = conf->chan;
2169                 item->confusers = conf->users;
2170                 item->announcetype = CONF_HASJOIN;
2171                 ast_mutex_lock(&conf->announcelistlock);
2172                 ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
2173                 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
2174                 ast_cond_signal(&conf->announcelist_addition);
2175                 ast_mutex_unlock(&conf->announcelistlock);
2176
2177                 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
2178                         ;
2179                 }
2180                 ao2_ref(item, -1);
2181         }
2182
2183         if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers)
2184                 dahdic.confmode = DAHDI_CONF_CONF;
2185         else if (confflags & CONFFLAG_MONITOR)
2186                 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2187         else if (confflags & CONFFLAG_TALKER)
2188                 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2189         else 
2190                 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2191
2192         if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2193                 ast_log(LOG_WARNING, "Error setting conference\n");
2194                 close(fd);
2195                 goto outrun;
2196         }
2197         ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
2198
2199         if (!sent_event) {
2200                 manager_event(EVENT_FLAG_CALL, "MeetmeJoin", 
2201                                 "Channel: %s\r\n"
2202                                 "Uniqueid: %s\r\n"
2203                                 "Meetme: %s\r\n"
2204                                 "Usernum: %d\r\n"
2205                                 "CallerIDnum: %s\r\n"
2206                                 "CallerIDname: %s\r\n",
2207                                 chan->name, chan->uniqueid, conf->confno, 
2208                                 user->user_no,
2209                                 S_OR(user->chan->cid.cid_num, "<unknown>"),
2210                                 S_OR(user->chan->cid.cid_name, "<unknown>")
2211                                 );
2212                 sent_event = 1;
2213         }
2214
2215         if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
2216                 firstpass = 1;
2217                 if (!(confflags & CONFFLAG_QUIET))
2218                         if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
2219                                 conf_play(chan, conf, ENTER);
2220         }
2221
2222         conf_flush(fd, chan);
2223
2224         if (confflags & CONFFLAG_AGI) {
2225                 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
2226                    or use default filename of conf-background.agi */
2227
2228                 ast_channel_lock(chan);
2229                 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
2230                         agifile = ast_strdupa(tmpvar);
2231                 } else {
2232                         agifile = ast_strdupa(agifiledefault);
2233                 }
2234                 ast_channel_unlock(chan);
2235                 
2236                 if (user->dahdichannel) {
2237                         /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
2238                         x = 1;
2239                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2240                 }
2241                 /* Find a pointer to the agi app and execute the script */
2242                 agi_app = pbx_findapp("agi");
2243                 if (agi_app) {
2244                         ret = pbx_exec(chan, agi_app, agifile);
2245                 } else {
2246                         ast_log(LOG_WARNING, "Could not find application (agi)\n");
2247                         ret = -2;
2248                 }
2249                 if (user->dahdichannel) {
2250                         /*  Remove CONFMUTE mode on DAHDI channel */
2251                         x = 0;
2252                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2253                 }
2254         } else {
2255                 if (user->dahdichannel && (confflags & CONFFLAG_STARMENU)) {
2256                         /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
2257                         x = 1;
2258                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2259                 }       
2260                 if (!(confflags & CONFFLAG_MONITOR) && !(dsp = ast_dsp_new())) {
2261                         ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
2262                         res = -1;
2263                 }
2264                 for (;;) {
2265                         int menu_was_active = 0;
2266
2267                         outfd = -1;
2268                         ms = -1;
2269                         now = ast_tvnow();
2270
2271                         if (rt_schedule && conf->endtime) {
2272                                 char currenttime[32];
2273                                 long localendtime = 0;
2274                                 int extended = 0;
2275                                 struct ast_tm tm;
2276                                 struct ast_variable *var, *origvar;
2277                                 struct timeval tmp;
2278
2279                                 if (now.tv_sec % 60 == 0) {
2280                                         if (!checked) {
2281                                                 ast_localtime(&now, &tm, NULL);
2282                                                 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2283                                                 var = origvar = ast_load_realtime("meetme", "confno",
2284                                                         conf->confno, "starttime <=", currenttime,
2285                                                          "endtime >=", currenttime, NULL);
2286
2287                                                 for ( ; var; var = var->next) {
2288                                                         if (!strcasecmp(var->name, "endtime")) {
2289                                                                 struct ast_tm endtime_tm;
2290                                                                 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
2291                                                                 tmp = ast_mktime(&endtime_tm, NULL);
2292                                                                 localendtime = tmp.tv_sec;
2293                                                         }
2294                                                 }
2295                                                 ast_variables_destroy(origvar);
2296
2297                                                 /* A conference can be extended from the
2298                                                    Admin/User menu or by an external source */
2299                                                 if (localendtime > conf->endtime){
2300                                                         conf->endtime = localendtime;
2301                                                         extended = 1;
2302                                                 }
2303
2304                                                 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
2305                                                         ast_verbose("Quitting time...\n");
2306                                                         goto outrun;
2307                                                 }
2308
2309                                                 if (!announcement_played && conf->endalert) {
2310                                                         if (now.tv_sec + conf->endalert >= conf->endtime) {
2311                                                                 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
2312                                                                         ast_waitstream(chan, "");
2313                                                                 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
2314                                                                 if (!ast_streamfile(chan, "minutes", chan->language))
2315                                                                         ast_waitstream(chan, "");
2316                                                                 announcement_played = 1;
2317                                                         }
2318                                                 }
2319
2320                                                 if (extended) {
2321                                                         announcement_played = 0;
2322                                                 }
2323
2324                                                 checked = 1;
2325                                         }
2326                                 } else {
2327                                         checked = 0;
2328                                 }
2329                         }
2330
2331                         if (user->kicktime && (user->kicktime <= now.tv_sec)) {
2332                                 break;
2333                         }
2334   
2335                         to = -1;
2336                         if (user->timelimit) {
2337                                 int minutes = 0, seconds = 0, remain = 0;
2338  
2339                                 to = ast_tvdiff_ms(nexteventts, now);
2340                                 if (to < 0) {
2341                                         to = 0;
2342                                 }
2343                                 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
2344                                 if (time_left_ms < to) {
2345                                         to = time_left_ms;
2346                                 }
2347         
2348                                 if (time_left_ms <= 0) {
2349                                         if (user->end_sound) {                                          
2350                                                 res = ast_streamfile(chan, user->end_sound, chan->language);
2351                                                 res = ast_waitstream(chan, "");
2352                                         }
2353                                         break;
2354                                 }
2355                                 
2356                                 if (!to) {
2357                                         if (time_left_ms >= 5000) {                                             
2358                                                 
2359                                                 remain = (time_left_ms + 500) / 1000;
2360                                                 if (remain / 60 >= 1) {
2361                                                         minutes = remain / 60;
2362                                                         seconds = remain % 60;
2363                                                 } else {
2364                                                         seconds = remain;
2365                                                 }
2366                                                 
2367                                                 /* force the time left to round up if appropriate */
2368                                                 if (user->warning_sound && user->play_warning) {
2369                                                         if (!strcmp(user->warning_sound, "timeleft")) {
2370                                                                 
2371                                                                 res = ast_streamfile(chan, "vm-youhave", chan->language);
2372                                                                 res = ast_waitstream(chan, "");
2373                                                                 if (minutes) {
2374                                                                         res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
2375                                                                         res = ast_streamfile(chan, "queue-minutes", chan->language);
2376                                                                         res = ast_waitstream(chan, "");
2377                                                                 }
2378                                                                 if (seconds) {
2379                                                                         res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
2380                                                                         res = ast_streamfile(chan, "queue-seconds", chan->language);
2381                                                                         res = ast_waitstream(chan, "");
2382                                                                 }
2383                                                         } else {
2384                                                                 res = ast_streamfile(chan, user->warning_sound, chan->language);
2385                                                                 res = ast_waitstream(chan, "");
2386                                                         }
2387                                                 }
2388                                         }
2389                                         if (user->warning_freq) {
2390                                                 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
2391                                         } else {
2392                                                 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2393                                         }
2394                                 }
2395                         }
2396
2397                         now = ast_tvnow();
2398                         if (timeout && now.tv_sec >= timeout) {
2399                                 break;
2400                         }
2401
2402                         /* if we have just exited from the menu, and the user had a channel-driver
2403                            volume adjustment, restore it
2404                         */
2405                         if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual) {
2406                                 set_talk_volume(user, user->listen.desired);
2407                         }
2408
2409                         menu_was_active = menu_active;
2410
2411                         currentmarked = conf->markedusers;
2412                         if (!(confflags & CONFFLAG_QUIET) &&
2413                             (confflags & CONFFLAG_MARKEDUSER) &&
2414                             (confflags & CONFFLAG_WAITMARKED) &&
2415                             lastmarked == 0) {
2416                                 if (currentmarked == 1 && conf->users > 1) {
2417                                         ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2418                                         if (conf->users - 1 == 1) {
2419                                                 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language)) {
2420                                                         ast_waitstream(chan, "");
2421                                                 }
2422                                         } else {
2423                                                 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language)) {
2424                                                         ast_waitstream(chan, "");
2425                                                 }
2426                                         }
2427                                 }
2428                                 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER)) {
2429                                         if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
2430                                                 ast_waitstream(chan, "");
2431                                         }
2432                                 }
2433                         }
2434
2435                         c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
2436
2437                         /* Update the struct with the actual confflags */
2438                         user->userflags = confflags;
2439
2440                         if (confflags & CONFFLAG_WAITMARKED) {
2441                                 if (currentmarked == 0) {
2442                                         if (lastmarked != 0) {
2443                                                 if (!(confflags & CONFFLAG_QUIET)) {
2444                                                         if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language)) {
2445                                                                 ast_waitstream(chan, "");
2446                                                         }
2447                                                 }
2448                                                 if (confflags & CONFFLAG_MARKEDEXIT) {
2449                                                         if (confflags & CONFFLAG_KICK_CONTINUE) {
2450                                                                 ret = 0;
2451                                                         }
2452                                                         break;
2453                                                 } else {
2454                                                         dahdic.confmode = DAHDI_CONF_CONF;
2455                                                         if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2456                                                                 ast_log(LOG_WARNING, "Error setting conference\n");
2457                                                                 close(fd);
2458                                                                 goto outrun;
2459                                                         }
2460                                                 }
2461                                         }
2462                                         if (!musiconhold && (confflags & CONFFLAG_MOH)) {
2463                                                 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2464                                                 musiconhold = 1;
2465                                         }
2466                                 } else if (currentmarked >= 1 && lastmarked == 0) {
2467                                         /* Marked user entered, so cancel timeout */
2468                                         timeout = 0;
2469                                         if (confflags & CONFFLAG_MONITOR) {
2470                                                 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2471                                         } else if (confflags & CONFFLAG_TALKER) {
2472                                                 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2473                                         } else {
2474                                                 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2475                                         }
2476                                         if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2477                                                 ast_log(LOG_WARNING, "Error setting conference\n");
2478                                                 close(fd);
2479                                                 goto outrun;
2480                                         }
2481                                         if (musiconhold && (confflags & CONFFLAG_MOH)) {
2482                                                 ast_moh_stop(chan);
2483                                                 musiconhold = 0;
2484                                         }
2485                                         if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
2486                                                 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language)) {
2487                                                         ast_waitstream(chan, "");
2488                                                 }
2489                                                 conf_play(chan, conf, ENTER);
2490                                         }
2491                                 }
2492                         }
2493
2494                         /* trying to add moh for single person conf */
2495                         if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
2496                                 if (conf->users == 1) {
2497                                         if (!musiconhold) {
2498                                                 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2499                                                 musiconhold = 1;
2500                                         } 
2501                                 } else {
2502                                         if (musiconhold) {
2503                                                 ast_moh_stop(chan);
2504                                                 musiconhold = 0;
2505                                         }
2506                                 }
2507                         }
2508                         
2509                         /* Leave if the last marked user left */
2510                         if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
2511                                 if (confflags & CONFFLAG_KICK_CONTINUE) {
2512                                         ret = 0;
2513                                 } else {
2514                                         ret = -1;
2515                                 }
2516                                 break;
2517                         }
2518         
2519                         /* Check if my modes have changed */
2520
2521                         /* If I should be muted but am still talker, mute me */
2522                         if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
2523                                 dahdic.confmode ^= DAHDI_CONF_TALKER;
2524                                 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2525                                         ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2526                                         ret = -1;
2527                                         break;
2528                                 }
2529
2530                                 manager_event(EVENT_FLAG_CALL, "MeetmeMute", 
2531                                                 "Channel: %s\r\n"
2532                                                 "Uniqueid: %s\r\n"
2533                                                 "Meetme: %s\r\n"
2534                                                 "Usernum: %i\r\n"
2535                                                 "Status: on\r\n",
2536                                                 chan->name, chan->uniqueid, conf->confno, user->user_no);
2537                         }
2538
2539                         /* If I should be un-muted but am not talker, un-mute me */
2540                         if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
2541                                 dahdic.confmode |= DAHDI_CONF_TALKER;
2542                                 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2543                                         ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
2544                                         ret = -1;
2545                                         break;
2546                                 }
2547
2548                                 manager_event(EVENT_FLAG_CALL, "MeetmeMute", 
2549                                                 "Channel: %s\r\n"
2550                                                 "Uniqueid: %s\r\n"
2551                                                 "Meetme: %s\r\n"
2552                                                 "Usernum: %i\r\n"
2553                                                 "Status: off\r\n",
2554                                                 chan->name, chan->uniqueid, conf->confno, user->user_no);
2555                         }
2556                         
2557                         if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
2558                                 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
2559                                 talkreq_manager = 1;
2560
2561                                 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest", 
2562                                               "Channel: %s\r\n"
2563                                                               "Uniqueid: %s\r\n"
2564                                                               "Meetme: %s\r\n"
2565                                                               "Usernum: %i\r\n"
2566                                                               "Status: on\r\n",
2567                                                               chan->name, chan->uniqueid, conf->confno, user->user_no);
2568                         }
2569
2570                         
2571                         if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
2572                                 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
2573                                 talkreq_manager = 0;
2574                                 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest", 
2575                                               "Channel: %s\r\n"
2576                                                               "Uniqueid: %s\r\n"
2577                                                               "Meetme: %s\r\n"
2578                                                               "Usernum: %i\r\n"
2579                                                               "Status: off\r\n",
2580                                                              chan->name, chan->uniqueid, conf->confno, user->user_no);
2581                         }
2582                         
2583                         /* If I have been kicked, exit the conference */
2584                         if (user->adminflags & ADMINFLAG_KICKME) {
2585                                 /* You have been kicked. */
2586                                 if (!(confflags & CONFFLAG_QUIET) && 
2587                                         !ast_streamfile(chan, "conf-kicked", chan->language)) {
2588                                         ast_waitstream(chan, "");
2589                                 }
2590                                 ret = 0;
2591                                 break;
2592                         }
2593
2594                         /* Perform an extra hangup check just in case */
2595                         if (ast_check_hangup(chan)) {
2596                                 break;
2597                         }
2598
2599                         if (c) {
2600                                 char dtmfstr[2] = "";
2601
2602                                 if (c->fds[0] != origfd || (user->dahdichannel && (c->audiohooks || c->monitor))) {
2603                                         if (using_pseudo) {
2604                                                 /* Kill old pseudo */
2605                                                 close(fd);
2606                                                 using_pseudo = 0;
2607                                         }
2608                                         ast_debug(1, "Ooh, something swapped out under us, starting over\n");
2609                                         retrydahdi = (strcasecmp(c->tech->type, "DAHDI") || (c->audiohooks || c->monitor) ? 1 : 0);
2610                                         user->dahdichannel = !retrydahdi;
2611                                         goto dahdiretry;
2612                                 }
2613                                 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2614                                         f = ast_read_noaudio(c);
2615                                 } else {
2616                                         f = ast_read(c);
2617                                 }
2618                                 if (!f) {
2619                                         break;
2620                                 }
2621                                 if (f->frametype == AST_FRAME_DTMF) {
2622                                         dtmfstr[0] = f->subclass;
2623                                         dtmfstr[1] = '\0';
2624                                 }
2625
2626                                 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
2627                                         if (user->talk.actual) {
2628                                                 ast_frame_adjust_volume(f, user->talk.actual);
2629                                         }
2630
2631                                         if (!(confflags & CONFFLAG_MONITOR)) {
2632                                                 int totalsilence;
2633
2634                                                 if (user->talking == -1) {
2635                                                         user->talking = 0;
2636                                                 }
2637
2638                                                 res = ast_dsp_silence(dsp, f, &totalsilence);
2639                                                 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
2640                                                         user->talking = 1;
2641                                                         if (confflags & CONFFLAG_MONITORTALKER)
2642                                                                 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2643                                                                       "Channel: %s\r\n"
2644                                                                       "Uniqueid: %s\r\n"
2645                                                                       "Meetme: %s\r\n"
2646                                                                       "Usernum: %d\r\n"
2647                                                                       "Status: on\r\n",
2648                                                                       chan->name, chan->uniqueid, conf->confno, user->user_no);
2649                                                 }
2650                                                 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
2651                                                         user->talking = 0;
2652                                                         if (confflags & CONFFLAG_MONITORTALKER) {
2653                                                                 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
2654                                                                       "Channel: %s\r\n"
2655                                                                       "Uniqueid: %s\r\n"
2656                                                                       "Meetme: %s\r\n"
2657                                                                       "Usernum: %d\r\n"
2658                                                                       "Status: off\r\n",
2659                                                                       chan->name, chan->uniqueid, conf->confno, user->user_no);
2660                                                         }
2661                                                 }
2662                                         }
2663                                         if (using_pseudo) {
2664                                                 /* Absolutely do _not_ use careful_write here...
2665                                                    it is important that we read data from the channel
2666                                                    as fast as it arrives, and feed it into the conference.
2667                                                    The buffering in the pseudo channel will take care of any
2668                                                    timing differences, unless they are so drastic as to lose
2669                                                    audio frames (in which case carefully writing would only
2670                                                    have delayed the audio even further).
2671                                                 */
2672                                                 /* As it turns out, we do want to use careful write.  We just
2673                                                    don't want to block, but we do want to at least *try*
2674                                                    to write out all the samples.
2675                                                  */
2676                                                 if (user->talking) {
2677                                                         careful_write(fd, f->data.ptr, f->datalen, 0);
2678                                                 }
2679                                         }
2680                                 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2681                                         if (confflags & CONFFLAG_PASS_DTMF) {
2682                                                 conf_queue_dtmf(conf, user, f);
2683                                         }
2684                                         if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
2685                                                 ast_log(LOG_WARNING, "Error setting conference\n");
2686                                                 close(fd);
2687                                                 ast_frfree(f);
2688                                                 goto outrun;
2689                                         }
2690
2691                                         /* if we are entering the menu, and the user has a channel-driver
2692                                            volume adjustment, clear it
2693                                         */
2694                                         if (!menu_active && user->talk.desired && !user->talk.actual) {
2695                                                 set_talk_volume(user, 0);
2696                                         }
2697
2698                                         if (musiconhold) {
2699                                                 ast_moh_stop(chan);
2700                                         }
2701                                         if ((confflags & CONFFLAG_ADMIN)) {
2702                                                 /* Admin menu */
2703                                                 if (!menu_active) {
2704                                                         menu_active = 1;
2705                                                         /* Record this sound! */
2706                                                         if (!ast_streamfile(chan, "conf-adminmenu-162", chan->language)) {
2707                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2708                                                                 ast_stopstream(chan);
2709                                                         } else {
2710                                                                 dtmf = 0;
2711                                                         }
2712                                                 } else {
2713                                                         dtmf = f->subclass;
2714                                                 }
2715                                                 if (dtmf) {
2716                                                         switch(dtmf) {
2717                                                         case '1': /* Un/Mute */
2718                                                                 menu_active = 0;
2719
2720                                                                 /* for admin, change both admin and use flags */
2721                                                                 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2722                                                                         user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2723                                                                 } else {
2724                                                                         user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2725                                                                 }
2726
2727                                                                 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2728                                                                         if (!ast_streamfile(chan, "conf-muted", chan->language)) {
2729                                                                                 ast_waitstream(chan, "");
2730                                                                         }
2731                                                                 } else {
2732                                                                         if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
2733                                                                                 ast_waitstream(chan, "");
2734                                                                         }
2735                                                                 }
2736                                                                 break;
2737                                                         case '2': /* Un/Lock the Conference */
2738                                                                 menu_active = 0;
2739                                                                 if (conf->locked) {
2740                                                                         conf->locked = 0;
2741                                                                         if (!ast_streamfile(chan, "conf-unlockednow", chan->language)) {
2742                                                                                 ast_waitstream(chan, "");
2743                                                                         }
2744                                                                 } else {
2745                                                                         conf->locked = 1;
2746                                                                         if (!ast_streamfile(chan, "conf-lockednow", chan->language)) {
2747                                                                                 ast_waitstream(chan, "");
2748                                                                         }
2749                                                                 }
2750                                                                 break;
2751                                                         case '3': /* Eject last user */
2752                                                                 menu_active = 0;
2753                                                                 usr = AST_LIST_LAST(&conf->userlist);
2754                                                                 if ((usr->chan->name == chan->name) || (usr->userflags & CONFFLAG_ADMIN)) {
2755                                                                         if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2756                                                                                 ast_waitstream(chan, "");
2757                                                                         }
2758                                                                 } else {
2759                                                                         usr->adminflags |= ADMINFLAG_KICKME;
2760                                                                 }
2761                                                                 ast_stopstream(chan);
2762                                                                 break;  
2763                                                         case '4':
2764                                                                 tweak_listen_volume(user, VOL_DOWN);
2765                                                                 break;
2766                                                         case '5':
2767                                                                 /* Extend RT conference */
2768                                                                 if (rt_schedule) {
2769                                                                         if (!rt_extend_conf(conf->confno)) {
2770                                                                                 if (!ast_streamfile(chan, "conf-extended", chan->language)) {
2771                                                                                         ast_waitstream(chan, "");
2772                                                                                 }
2773                                                                         } else {
2774                                                                                 if (!ast_streamfile(chan, "conf-nonextended", chan->language)) {
2775                                                                                         ast_waitstream(chan, "");
2776                                                                                 }
2777                                                                         }
2778                                                                         ast_stopstream(chan);
2779                                                                 }
2780                                                                 menu_active = 0;
2781                                                                 break;
2782                                                         case '6':
2783                                                                 tweak_listen_volume(user, VOL_UP);
2784                                                                 break;
2785                                                         case '7':
2786                                                                 tweak_talk_volume(user, VOL_DOWN);
2787                                                                 break;
2788                                                         case '8':
2789                                                                 menu_active = 0;
2790                                                                 break;
2791                                                         case '9':
2792                                                                 tweak_talk_volume(user, VOL_UP);
2793                                                                 break;
2794                                                         default:
2795                                                                 menu_active = 0;
2796                                                                 /* Play an error message! */
2797                                                                 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2798                                                                         ast_waitstream(chan, "");
2799                                                                 }
2800                                                                 break;
2801                                                         }
2802                                                 }
2803                                         } else {
2804                                                 /* User menu */
2805                                                 if (!menu_active) {
2806                                                         menu_active = 1;
2807                                                         if (!ast_streamfile(chan, "conf-usermenu-162", chan->language)) {
2808                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2809                                                                 ast_stopstream(chan);
2810                                                         } else {
2811                                                                 dtmf = 0;
2812                                                         }
2813                                                 } else {
2814                                                         dtmf = f->subclass;
2815                                                 }
2816                                                 if (dtmf) {
2817                                                         switch (dtmf) {
2818                                                         case '1': /* Un/Mute */
2819                                                                 menu_active = 0;
2820
2821                                                                 /* user can only toggle the self-muted state */
2822                                                                 user->adminflags ^= ADMINFLAG_SELFMUTED;
2823
2824                                                                 /* they can't override the admin mute state */
2825                                                                 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2826                                                                         if (!ast_streamfile(chan, "conf-muted", chan->language)) {
2827                                                                                 ast_waitstream(chan, "");
2828                                                                         }
2829                                                                 } else {
2830                                                                         if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
2831                                                                                 ast_waitstream(chan, "");
2832                                                                         }
2833                                                                 }
2834                                                                 break;
2835                                                         case '2':
2836                                                                 menu_active = 0;
2837                                                                 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
2838                                                                         user->adminflags |= ADMINFLAG_T_REQUEST;
2839                                                                 }
2840                                                                         
2841                                                                 if (user->adminflags & ADMINFLAG_T_REQUEST) {
2842                                                                         if (!ast_streamfile(chan, "beep", chan->language)) {
2843                                                                                 ast_waitstream(chan, "");
2844                                                                         }
2845                                                                 }
2846                                                                 break;
2847                                                         case '4':
2848                                                                 tweak_listen_volume(user, VOL_DOWN);
2849                                                                 break;
2850                                                         case '5':
2851                                                                 /* Extend RT conference */
2852                                                                 if (rt_schedule) {
2853                                                                         rt_extend_conf(conf->confno);
2854                                                                 }
2855                                                                 menu_active = 0;
2856                                                                 break;
2857                                                         case '6':
2858                                                                 tweak_listen_volume(user, VOL_UP);
2859                                                                 break;
2860                                                         case '7':
2861                                                                 tweak_talk_volume(user, VOL_DOWN);
2862                                                                 break;
2863                                                         case '8':
2864                                                                 menu_active = 0;
2865                                                                 break;
2866                                                         case '9':
2867                                                                 tweak_talk_volume(user, VOL_UP);
2868                                                                 break;
2869                                                         default:
2870                                                                 menu_active = 0;
2871                                                                 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
2872                                                                         ast_waitstream(chan, "");
2873                                                                 }
2874                                                                 break;
2875                                                         }
2876                                                 }
2877                                         }
2878                                         if (musiconhold) {
2879                                                 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2880                                         }
2881
2882                                         if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2883                                                 ast_log(LOG_WARNING, "Error setting conference\n");
2884                                                 close(fd);
2885                                                 ast_frfree(f);
2886                                                 goto outrun;
2887                                         }
2888
2889                                         conf_flush(fd, chan);
2890                                 /* Since this option could absorb DTMF meant for the previous (menu), we have to check this one last */
2891                                 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
2892                                         if (confflags & CONFFLAG_PASS_DTMF) {
2893                                                 conf_queue_dtmf(conf, user, f);
2894                                         }
2895
2896                                         if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
2897                                                 ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
2898                                                 ret = 0;
2899                                                 ast_frfree(f);
2900                                                 break;
2901                                         } else {
2902                                                 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
2903                                         }
2904                                 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
2905                                         pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
2906                                                 
2907                                         if (confflags & CONFFLAG_PASS_DTMF) {
2908                                                 conf_queue_dtmf(conf, user, f);
2909                                         }
2910                                         ret = 0;
2911                                         ast_frfree(f);
2912                                         break;
2913                                 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2914                                         && confflags & CONFFLAG_PASS_DTMF) {
2915                                         conf_queue_dtmf(conf, user, f);
2916                                 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2917                                         switch (f->subclass) {
2918                                         case AST_CONTROL_HOLD:
2919                                                 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2920                                                 break;
2921                                         default:
2922                                                 break;
2923                                         }
2924                                 } else if (f->frametype == AST_FRAME_NULL) {
2925                                         /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2926                                 } else {
2927                                         ast_debug(1, 
2928                                                 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2929                                                 chan->name, f->frametype, f->subclass);
2930                                 }
2931                                 ast_frfree(f);
2932                         } else if (outfd > -1) {
2933                                 res = read(outfd, buf, CONF_SIZE);
2934                                 if (res > 0) {
2935                                         memset(&fr, 0, sizeof(fr));
2936                                         fr.frametype = AST_FRAME_VOICE;
2937                                         fr.subclass = AST_FORMAT_SLINEAR;
2938                                         fr.datalen = res;
2939                                         fr.samples = res / 2;
2940                                         fr.data.ptr = buf;
2941                                         fr.offset = AST_FRIENDLY_OFFSET;
2942                                         if (!user->listen.actual && 
2943                                                 ((confflags & CONFFLAG_MONITOR) || 
2944                                                  (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2945                                                  (!user->talking)) ) {
2946                                                 int idx;
2947                                                 for (idx = 0; idx < AST_FRAME_BITS; idx++) {
2948                                                         if (chan->rawwriteformat & (1 << idx)) {
2949                                                                 break;
2950                                                         }
2951                                                 }
2952                                                 if (idx >= AST_FRAME_BITS) {
2953                                                         goto bailoutandtrynormal;
2954                                                 }
2955                                                 ast_mutex_lock(&conf->listenlock);
2956                                                 if (!conf->transframe[idx]) {
2957                                                         if (conf->origframe) {
2958                                                                 if (!conf->transpath[idx]) {
2959                                                                         conf->transpath[idx] = ast_translator_build_path((1 << idx), AST_FORMAT_SLINEAR);
2960                                                                 }
2961                                                                 if (conf->transpath[idx]) {
2962                                                                         conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
2963                                                                         if (!conf->transframe[idx]) {
2964                                                                                 conf->transframe[idx] = &ast_null_frame;
2965                                                                         }
2966                                                                 }
2967                                                         }
2968                                                 }
2969                                                 if (conf->transframe[idx]) {
2970                                                         if (conf->transframe[idx]->frametype != AST_FRAME_NULL) {
2971                                                                 if (ast_write(chan, conf->transframe[idx])) {
2972                                                                         ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2973                                                                 }
2974                                                         }
2975                                                 } else {
2976                                                         ast_mutex_unlock(&conf->listenlock);
2977                                                         goto bailoutandtrynormal;
2978                                                 }
2979                                                 ast_mutex_unlock(&conf->listenlock);
2980                                         } else {
2981 bailoutandtrynormal:                                    
2982                                                 if (user->listen.actual) {
2983                                                         ast_frame_adjust_volume(&fr, user->listen.actual);
2984                                                 }
2985                                                 if (ast_write(chan, &fr) < 0) {
2986                                                         ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2987                                                 }
2988                                         }
2989                                 } else {
2990                                         ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2991                                 }
2992                         }
2993                         lastmarked = currentmarked;
2994                 }
2995         }
2996
2997         if (musiconhold) {
2998                 ast_moh_stop(chan);
2999         }
3000         
3001         if (using_pseudo) {
3002                 close(fd);
3003         } else {
3004                 /* Take out of conference */
3005                 dahdic.chan = 0;        
3006                 dahdic.confno = 0;
3007                 dahdic.confmode = 0;
3008                 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
3009                         ast_log(LOG_WARNING, "Error setting conference\n");
3010                 }
3011         }
3012
3013         reset_volumes(user);
3014
3015         if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
3016                 conf_play(chan, conf, LEAVE);
3017         }
3018
3019         if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
3020                 struct announce_listitem *item;
3021                 if (!(item = ao2_alloc(sizeof(*item), NULL)))
3022                         return -1;
3023                 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
3024                 ast_copy_string(item->language, chan->language, sizeof(item->language));
3025                 item->confchan = conf->chan;
3026                 item->confusers = conf->users;
3027                 item->announcetype = CONF_HASLEFT;
3028                 ast_mutex_lock(&conf->announcelistlock);
3029                 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
3030                 ast_cond_signal(&conf->announcelist_addition);
3031                 ast_mutex_unlock(&conf->announcelistlock);
3032         } else if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users == 1) {
3033                 /* Last person is leaving, so no reason to try and announce, but should delete the name recording */
3034                 ast_filedelete(user->namerecloc, NULL);
3035         }
3036
3037  outrun:
3038         AST_LIST_LOCK(&confs);
3039
3040         if (dsp) {
3041                 ast_dsp_free(dsp);
3042         }
3043         
3044         if (user->user_no) { /* Only cleanup users who really joined! */
3045                 now = ast_tvnow();
3046                 hr = (now.tv_sec - user->jointime) / 3600;
3047                 min = ((now.tv_sec - user->jointime) % 3600) / 60;
3048                 sec = (now.tv_sec - user->jointime) % 60;
3049
3050                 if (sent_event) {
3051                         manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
3052                                       "Channel: %s\r\n"
3053                                       "Uniqueid: %s\r\n"
3054                                       "Meetme: %s\r\n"
3055                                       "Usernum: %d\r\n"
3056                                       "CallerIDNum: %s\r\n"
3057                                       "CallerIDName: %s\r\n"
3058                                       "Duration: %ld\r\n",
3059                                       chan->name, chan->uniqueid, conf->confno, 
3060                                       user->user_no,
3061                                       S_OR(user->chan->cid.cid_num, "<unknown>"),
3062                                       S_OR(user->chan->cid.cid_name, "<unknown>"),
3063                                       (l