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