d05c37eb930a83753bdcb32309b79319ed8d887f
[asterisk/asterisk.git] / apps / app_meetme.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Meet me conference bridge
22  *
23  * \author Mark Spencer <markster@digium.com>
24  * 
25  * \ingroup applications
26  */
27
28 /*** MODULEINFO
29         <depend>zaptel</depend>
30  ***/
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <sys/ioctl.h>
38 #ifdef __linux__
39 #include <linux/zaptel.h>
40 #else
41 #include <zaptel.h>
42 #endif /* __linux__ */
43
44 #include "asterisk.h"
45
46 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
47
48 #include "asterisk/lock.h"
49 #include "asterisk/file.h"
50 #include "asterisk/logger.h"
51 #include "asterisk/channel.h"
52 #include "asterisk/pbx.h"
53 #include "asterisk/module.h"
54 #include "asterisk/config.h"
55 #include "asterisk/app.h"
56 #include "asterisk/dsp.h"
57 #include "asterisk/musiconhold.h"
58 #include "asterisk/manager.h"
59 #include "asterisk/options.h"
60 #include "asterisk/cli.h"
61 #include "asterisk/say.h"
62 #include "asterisk/utils.h"
63 #include "asterisk/translate.h"
64 #include "asterisk/ulaw.h"
65
66 #include "enter.h"
67 #include "leave.h"
68
69 LOCAL_USER_DECL;
70
71 #define CONFIG_FILE_NAME "meetme.conf"
72
73 /*! each buffer is 20ms, so this is 640ms total */
74 #define DEFAULT_AUDIO_BUFFERS  32
75
76 enum {
77         ADMINFLAG_MUTED =  (1 << 1), /*!< User is muted */
78         ADMINFLAG_KICKME = (1 << 2)  /*!< User has been kicked */
79 };
80
81 #define MEETME_DELAYDETECTTALK     300
82 #define MEETME_DELAYDETECTENDTALK  1000
83
84 #define AST_FRAME_BITS  32
85
86 enum volume_action {
87         VOL_UP,
88         VOL_DOWN
89 };
90
91 enum entrance_sound {
92         ENTER,
93         LEAVE
94 };
95
96 enum recording_state {
97         MEETME_RECORD_OFF,
98         MEETME_RECORD_STARTED,
99         MEETME_RECORD_ACTIVE,
100         MEETME_RECORD_TERMINATE
101 };
102
103 #define CONF_SIZE  320
104
105 enum {
106         /*! user has admin access on the conference */
107         CONFFLAG_ADMIN = (1 << 0),
108         /*! If set the user can only receive audio from the conference */
109         CONFFLAG_MONITOR = (1 << 1),
110         /*! If set asterisk will exit conference when '#' is pressed */
111         CONFFLAG_POUNDEXIT = (1 << 2),
112         /*! If set asterisk will provide a menu to the user when '*' is pressed */
113         CONFFLAG_STARMENU = (1 << 3),
114         /*! If set the use can only send audio to the conference */
115         CONFFLAG_TALKER = (1 << 4),
116         /*! If set there will be no enter or leave sounds */
117         CONFFLAG_QUIET = (1 << 5),
118         /*! If set, when user joins the conference, they will be told the number 
119          *  of users that are already in */
120         CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
121         /*! Set to run AGI Script in Background */
122         CONFFLAG_AGI = (1 << 7),
123         /*! Set to have music on hold when user is alone in conference */
124         CONFFLAG_MOH = (1 << 8),
125         /*! If set the MeetMe will return if all marked with this flag left */
126         CONFFLAG_MARKEDEXIT = (1 << 9),
127         /*! If set, the MeetMe will wait until a marked user enters */
128         CONFFLAG_WAITMARKED = (1 << 10),
129         /*! If set, the MeetMe will exit to the specified context */
130         CONFFLAG_EXIT_CONTEXT = (1 << 11),
131         /*! If set, the user will be marked */
132         CONFFLAG_MARKEDUSER = (1 << 12),
133         /*! If set, user will be ask record name on entry of conference */
134         CONFFLAG_INTROUSER = (1 << 13),
135         /*! If set, the MeetMe will be recorded */
136         CONFFLAG_RECORDCONF = (1<< 14),
137         /*! If set, the user will be monitored if the user is talking or not */
138         CONFFLAG_MONITORTALKER = (1 << 15),
139         CONFFLAG_DYNAMIC = (1 << 16),
140         CONFFLAG_DYNAMICPIN = (1 << 17),
141         CONFFLAG_EMPTY = (1 << 18),
142         CONFFLAG_EMPTYNOPIN = (1 << 19),
143         CONFFLAG_ALWAYSPROMPT = (1 << 20),
144         /*! If set, treats talking users as muted users */
145         CONFFLAG_OPTIMIZETALKER = (1 << 21),
146         /*! If set, won't speak the extra prompt when the first person 
147          *  enters the conference */
148         CONFFLAG_NOONLYPERSON = (1 << 22),
149         CONFFLAG_INTROUSERNOREVIEW = (1 << 23)
150         /*! If set, user will be asked to record name on entry of conference 
151          *  without review */
152 };
153
154 AST_APP_OPTIONS(meetme_opts, {
155         AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
156         AST_APP_OPTION('a', CONFFLAG_ADMIN ),
157         AST_APP_OPTION('b', CONFFLAG_AGI ),
158         AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
159         AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
160         AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
161         AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
162         AST_APP_OPTION('e', CONFFLAG_EMPTY ),
163         AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
164         AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
165         AST_APP_OPTION('M', CONFFLAG_MOH ),
166         AST_APP_OPTION('m', CONFFLAG_MONITOR ),
167         AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
168         AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
169         AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ),
170         AST_APP_OPTION('q', CONFFLAG_QUIET ),
171         AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
172         AST_APP_OPTION('s', CONFFLAG_STARMENU ),
173         AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
174         AST_APP_OPTION('t', CONFFLAG_TALKER ),
175         AST_APP_OPTION('w', CONFFLAG_WAITMARKED ),
176         AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
177         AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
178         AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
179 });
180
181 static const char *app = "MeetMe";
182 static const char *app2 = "MeetMeCount";
183 static const char *app3 = "MeetMeAdmin";
184
185 static const char *synopsis = "MeetMe conference bridge";
186 static const char *synopsis2 = "MeetMe participant count";
187 static const char *synopsis3 = "MeetMe conference Administration";
188
189 static const char *descrip =
190 "  MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
191 "conference.  If the conference number is omitted, the user will be prompted\n"
192 "to enter one.  User can exit the conference by hangup, or if the 'p' option\n"
193 "is specified, by pressing '#'.\n"
194 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
195 "             must be present for conferencing to operate properly. In addition, the chan_zap\n"
196 "             channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
197 "The option string may contain zero or more of the following characters:\n"
198 "      'a' -- set admin mode\n"
199 "      'A' -- set marked mode\n"
200 "      'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
201 "             Default: conf-background.agi  (Note: This does not work with\n"
202 "             non-Zap channels in the same conference)\n"
203 "      'c' -- announce user(s) count on joining a conference\n"
204 "      'd' -- dynamically add conference\n"
205 "      'D' -- dynamically add conference, prompting for a PIN\n"
206 "      'e' -- select an empty conference\n"
207 "      'E' -- select an empty pinless conference\n"
208 "      'i' -- announce user join/leave with review\n"
209 "      'I' -- announce user join/leave without review\n"
210 "      'm' -- set monitor only mode (Listen only, no talking)\n"
211 "      'M' -- enable music on hold when the conference has a single caller\n"
212 "      'o' -- set talker optimization - treats talkers who aren't speaking as\n"
213 "             being muted, meaning (a) No encode is done on transmission and\n"
214 "             (b) Received audio that is not registered as talking is omitted\n"
215 "             causing no buildup in background noise\n"
216 "      'p' -- allow user to exit the conference by pressing '#'\n"
217 "      'P' -- always prompt for the pin even if it is specified\n"
218 "      'q' -- quiet mode (don't play enter/leave sounds)\n"
219 "      'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
220 "             using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
221 "             meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
222 "             wav.\n"
223 "      's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
224 "      't' -- set talk only mode. (Talk only, no listening)\n"
225 "      'T' -- set talker detection (sent to manager interface and meetme list)\n"
226 "      'w' -- wait until the marked user enters the conference\n"
227 "      'x' -- close the conference when last marked user exits\n"
228 "      'X' -- allow user to exit the conference by entering a valid single\n"
229 "             digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
230 "             if that variable is not defined.\n"
231 "      '1' -- do not play message when first person enters\n";
232
233 static const char *descrip2 =
234 "  MeetMeCount(confno[|var]): Plays back the number of users in the specified\n"
235 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
236 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
237 "the channel, unless priority n+1 exists, in which case priority progress will\n"
238 "continue.\n"
239 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
240
241 static const char *descrip3 = 
242 "  MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
243 "      'e' -- Eject last user that joined\n"
244 "      'k' -- Kick one user out of conference\n"
245 "      'K' -- Kick all users out of conference\n"
246 "      'l' -- Unlock conference\n"
247 "      'L' -- Lock conference\n"
248 "      'm' -- Unmute conference\n"
249 "      'M' -- Mute conference\n"
250 "      'n' -- Unmute entire conference (except admin)\n"
251 "      'N' -- Mute entire conference (except admin)\n"
252 "";
253
254 struct ast_conference {
255         ast_mutex_t playlock;                   /*!< Conference specific lock (players) */
256         ast_mutex_t listenlock;                 /*!< Conference specific lock (listeners) */
257         char confno[AST_MAX_EXTENSION];         /*!< Conference */
258         struct ast_channel *chan;               /*!< Announcements channel */
259         struct ast_channel *lchan;              /*!< Listen/Record channel */
260         int fd;                                 /*!< Announcements fd */
261         int zapconf;                            /*!< Zaptel Conf # */
262         int users;                              /*!< Number of active users */
263         int markedusers;                        /*!< Number of marked users */
264         time_t start;                           /*!< Start time (s) */
265         int refcount;                           /*!< reference count of usage */
266         enum recording_state recording:2;               /*!< recording status */
267         unsigned int isdynamic:1;               /*!< Created on the fly? */
268         unsigned int locked:1;                  /*!< Is the conference locked? */
269         pthread_t recordthread;                 /*!< thread for recording */
270         pthread_attr_t attr;                    /*!< thread attribute */
271         const char *recordingfilename;          /*!< Filename to record the Conference into */
272         const char *recordingformat;            /*!< Format to record the Conference in */
273         char pin[AST_MAX_EXTENSION];            /*!< If protected by a PIN */
274         char pinadmin[AST_MAX_EXTENSION];       /*!< If protected by a admin PIN */
275         struct ast_frame *transframe[32];
276         struct ast_frame *origframe;
277         struct ast_trans_pvt *transpath[32];
278         AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
279         AST_LIST_ENTRY(ast_conference) list;
280 };
281
282 static AST_LIST_HEAD_STATIC(confs, ast_conference);
283
284 struct volume {
285         int desired;                            /*!< Desired volume adjustment */
286         int actual;                             /*!< Actual volume adjustment (for channels that can't adjust) */
287 };
288
289 struct ast_conf_user {
290         int user_no;                            /*!< User Number */
291         int userflags;                          /*!< Flags as set in the conference */
292         int adminflags;                         /*!< Flags set by the Admin */
293         struct ast_channel *chan;               /*!< Connected channel */
294         int talking;                            /*!< Is user talking */
295         int zapchannel;                         /*!< Is a Zaptel channel */
296         char usrvalue[50];                      /*!< Custom User Value */
297         char namerecloc[AST_MAX_EXTENSION];     /*!< Name Recorded file Location */
298         time_t jointime;                        /*!< Time the user joined the conference */
299         struct volume talk;
300         struct volume listen;
301         AST_LIST_ENTRY(ast_conf_user) list;
302 };
303
304 /*! The number of audio buffers to be allocated on pseudo channels
305  *  when in a conference */
306 static int audio_buffers;
307
308 /*! Map 'volume' levels from -5 through +5 into
309  *  decibel (dB) settings for channel drivers
310  *  Note: these are not a straight linear-to-dB
311  *  conversion... the numbers have been modified
312  *  to give the user a better level of adjustability
313  */
314 static signed char gain_map[] = {
315         -15,
316         -13,
317         -10,
318         -6,
319         0,
320         0,
321         0,
322         6,
323         10,
324         13,
325         15,
326 };
327
328
329 static int admin_exec(struct ast_channel *chan, void *data);
330 static void *recordthread(void *args);
331
332 static char *istalking(int x)
333 {
334         if (x > 0)
335                 return "(talking)";
336         else if (x < 0)
337                 return "(unmonitored)";
338         else 
339                 return "(not talking)";
340 }
341
342 static int careful_write(int fd, unsigned char *data, int len, int block)
343 {
344         int res;
345         int x;
346
347         while (len) {
348                 if (block) {
349                         x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
350                         res = ioctl(fd, ZT_IOMUX, &x);
351                 } else
352                         res = 0;
353                 if (res >= 0)
354                         res = write(fd, data, len);
355                 if (res < 1) {
356                         if (errno != EAGAIN) {
357                                 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
358                                 return -1;
359                         } else
360                                 return 0;
361                 }
362                 len -= res;
363                 data += res;
364         }
365
366         return 0;
367 }
368
369 static int set_talk_volume(struct ast_conf_user *user, int volume)
370 {
371         signed char gain_adjust;
372
373         /* attempt to make the adjustment in the channel driver;
374            if successful, don't adjust in the frame reading routine
375         */
376         gain_adjust = gain_map[volume + 5];
377
378         return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
379 }
380
381 static int set_listen_volume(struct ast_conf_user *user, int volume)
382 {
383         signed char gain_adjust;
384
385         /* attempt to make the adjustment in the channel driver;
386            if successful, don't adjust in the frame reading routine
387         */
388         gain_adjust = gain_map[volume + 5];
389
390         return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
391 }
392
393 static void tweak_volume(struct volume *vol, enum volume_action action)
394 {
395         switch (action) {
396         case VOL_UP:
397                 switch (vol->desired) {
398                 case 5:
399                         break;
400                 case 0:
401                         vol->desired = 2;
402                         break;
403                 case -2:
404                         vol->desired = 0;
405                         break;
406                 default:
407                         vol->desired++;
408                         break;
409                 }
410                 break;
411         case VOL_DOWN:
412                 switch (vol->desired) {
413                 case -5:
414                         break;
415                 case 2:
416                         vol->desired = 0;
417                         break;
418                 case 0:
419                         vol->desired = -2;
420                         break;
421                 default:
422                         vol->desired--;
423                         break;
424                 }
425         }
426 }
427
428 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
429 {
430         tweak_volume(&user->talk, action);
431         /* attempt to make the adjustment in the channel driver;
432            if successful, don't adjust in the frame reading routine
433         */
434         if (!set_talk_volume(user, user->talk.desired))
435                 user->talk.actual = 0;
436         else
437                 user->talk.actual = user->talk.desired;
438 }
439
440 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
441 {
442         tweak_volume(&user->listen, action);
443         /* attempt to make the adjustment in the channel driver;
444            if successful, don't adjust in the frame reading routine
445         */
446         if (!set_listen_volume(user, user->listen.desired))
447                 user->listen.actual = 0;
448         else
449                 user->listen.actual = user->listen.desired;
450 }
451
452 static void reset_volumes(struct ast_conf_user *user)
453 {
454         signed char zero_volume = 0;
455
456         ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
457         ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
458 }
459
460 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
461 {
462         unsigned char *data;
463         int len;
464         int res = -1;
465
466         if (!chan->_softhangup)
467                 res = ast_autoservice_start(chan);
468
469         AST_LIST_LOCK(&confs);
470
471         switch(sound) {
472         case ENTER:
473                 data = enter;
474                 len = sizeof(enter);
475                 break;
476         case LEAVE:
477                 data = leave;
478                 len = sizeof(leave);
479                 break;
480         default:
481                 data = NULL;
482                 len = 0;
483         }
484         if (data) {
485                 careful_write(conf->fd, data, len, 1);
486         }
487
488         AST_LIST_UNLOCK(&confs);
489
490         if (!res) 
491                 ast_autoservice_stop(chan);
492 }
493
494 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount)
495 {
496         struct ast_conference *cnf;
497         struct zt_confinfo ztc;
498
499         AST_LIST_LOCK(&confs);
500
501         AST_LIST_TRAVERSE(&confs, cnf, list) {
502                 if (!strcmp(confno, cnf->confno)) 
503                         break;
504         }
505
506         if (!cnf && (make || dynamic)) {
507                 /* Make a new one */
508                 if ((cnf = ast_calloc(1, sizeof(*cnf)))) {
509                         ast_mutex_init(&cnf->playlock);
510                         ast_mutex_init(&cnf->listenlock);
511                         ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
512                         ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
513                         ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
514                         cnf->refcount = 0;
515                         cnf->markedusers = 0;
516                         cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
517                         if (cnf->chan) {
518                                 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
519                                 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
520                                 cnf->fd = cnf->chan->fds[0];    /* for use by conf_play() */
521                         } else {
522                                 ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
523                                 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
524                                 if (cnf->fd < 0) {
525                                         ast_log(LOG_WARNING, "Unable to open pseudo device\n");
526                                         free(cnf);
527                                         cnf = NULL;
528                                         goto cnfout;
529                                 }
530                         }
531                         memset(&ztc, 0, sizeof(ztc));
532                         /* Setup a new zap conference */
533                         ztc.chan = 0;
534                         ztc.confno = -1;
535                         ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
536                         if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
537                                 ast_log(LOG_WARNING, "Error setting conference\n");
538                                 if (cnf->chan)
539                                         ast_hangup(cnf->chan);
540                                 else
541                                         close(cnf->fd);
542                                 free(cnf);
543                                 cnf = NULL;
544                                 goto cnfout;
545                         }
546                         cnf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
547                         if (cnf->lchan) {
548                                 ast_set_read_format(cnf->lchan, AST_FORMAT_SLINEAR);
549                                 ast_set_write_format(cnf->lchan, AST_FORMAT_SLINEAR);
550                                 ztc.chan = 0;
551                                 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
552                                 if (ioctl(cnf->lchan->fds[0], ZT_SETCONF, &ztc)) {
553                                         ast_log(LOG_WARNING, "Error setting conference\n");
554                                         ast_hangup(cnf->lchan);
555                                         cnf->lchan = NULL;
556                                 }
557                         }
558                         /* Fill the conference struct */
559                         cnf->start = time(NULL);
560                         cnf->zapconf = ztc.confno;
561                         cnf->isdynamic = dynamic ? 1 : 0;
562                         if (option_verbose > 2)
563                                 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
564                         AST_LIST_INSERT_HEAD(&confs, cnf, list);
565                 } 
566         }
567  cnfout:
568         if (cnf){ 
569                 cnf->refcount += refcount;
570         }
571         AST_LIST_UNLOCK(&confs);
572         return cnf;
573 }
574
575 static int conf_cmd(int fd, int argc, char **argv) {
576         /* Process the command */
577         struct ast_conference *cnf;
578         struct ast_conf_user *user;
579         int hr, min, sec;
580         int i = 0, total = 0;
581         time_t now;
582         char *header_format = "%-14s %-14s %-10s %-8s  %-8s\n";
583         char *data_format = "%-12.12s   %4.4d         %4.4s       %02d:%02d:%02d  %-8s\n";
584         char cmdline[1024] = "";
585
586         if (argc > 8)
587                 ast_cli(fd, "Invalid Arguments.\n");
588         /* Check for length so no buffer will overflow... */
589         for (i = 0; i < argc; i++) {
590                 if (strlen(argv[i]) > 100)
591                         ast_cli(fd, "Invalid Arguments.\n");
592         }
593         if (argc == 1) {
594                 /* 'MeetMe': List all the conferences */        
595                 now = time(NULL);
596                 if (AST_LIST_EMPTY(&confs)) {
597                         ast_cli(fd, "No active MeetMe conferences.\n");
598                         return RESULT_SUCCESS;
599                 }
600                 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
601                 AST_LIST_TRAVERSE(&confs, cnf, list) {
602                         if (cnf->markedusers == 0)
603                                 strcpy(cmdline, "N/A ");
604                         else 
605                                 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
606                         hr = (now - cnf->start) / 3600;
607                         min = ((now - cnf->start) % 3600) / 60;
608                         sec = (now - cnf->start) % 60;
609
610                         ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
611
612                         total += cnf->users;    
613                 }
614                 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
615                 return RESULT_SUCCESS;
616         }
617         if (argc < 3)
618                 return RESULT_SHOWUSAGE;
619         ast_copy_string(cmdline, argv[2], sizeof(cmdline));     /* Argv 2: conference number */
620         if (strstr(argv[1], "lock")) {  
621                 if (strcmp(argv[1], "lock") == 0) {
622                         /* Lock */
623                         strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
624                 } else {
625                         /* Unlock */
626                         strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
627                 }
628         } else if (strstr(argv[1], "mute")) { 
629                 if (argc < 4)
630                         return RESULT_SHOWUSAGE;
631                 if (strcmp(argv[1], "mute") == 0) {
632                         /* Mute */
633                         if (strcmp(argv[3], "all") == 0) {
634                                 strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
635                         } else {
636                                 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1); 
637                                 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
638                         }
639                 } else {
640                         /* Unmute */
641                         if (strcmp(argv[3], "all") == 0) {
642                                 strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
643                         } else {
644                                 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
645                                 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
646                         }
647                 }
648         } else if (strcmp(argv[1], "kick") == 0) {
649                 if (argc < 4)
650                         return RESULT_SHOWUSAGE;
651                 if (strcmp(argv[3], "all") == 0) {
652                         /* Kick all */
653                         strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
654                 } else {
655                         /* Kick a single user */
656                         strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
657                         strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
658                 }       
659         } else if(strcmp(argv[1], "list") == 0) {
660                 int concise = ( 4 == argc && ( !strcasecmp(argv[3], "concise") ) );
661                 /* List all the users in a conference */
662                 if (AST_LIST_EMPTY(&confs)) {
663                         if ( !concise )
664                                 ast_cli(fd, "No active conferences.\n");
665                         return RESULT_SUCCESS;  
666                 }
667                 /* Find the right conference */
668                 AST_LIST_TRAVERSE(&confs, cnf, list) {
669                         if (strcmp(cnf->confno, argv[2]) == 0)
670                                 break;
671                 }
672                 if (!cnf) {
673                         if ( !concise )
674                                 ast_cli(fd, "No such conference: %s.\n",argv[2]);
675                         return RESULT_SUCCESS;
676                 }
677                 /* Show all the users */
678                 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
679                         now = time(NULL);
680                         hr = (now - user->jointime) / 3600;
681                         min = ((now - user->jointime) % 3600) / 60;
682                         sec = (now - user->jointime) % 60;
683                         if ( !concise )
684                                 ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %02d:%02d:%02d\n",
685                                         user->user_no,
686                                         S_OR(user->chan->cid.cid_num, "<unknown>"),
687                                         S_OR(user->chan->cid.cid_name, "<no name>"),
688                                         user->chan->name,
689                                         user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
690                                         user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
691                                         user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : "",
692                                         istalking(user->talking), hr, min, sec);
693                         else 
694                                 ast_cli(fd, "%d!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
695                                         user->user_no,
696                                         S_OR(user->chan->cid.cid_num, ""),
697                                         S_OR(user->chan->cid.cid_name, ""),
698                                         user->chan->name,
699                                         user->userflags  & CONFFLAG_ADMIN   ? "1" : "",
700                                         user->userflags  & CONFFLAG_MONITOR ? "1" : "",
701                                         user->adminflags & ADMINFLAG_MUTED  ? "1" : "",
702                                         user->talking, hr, min, sec);
703                         
704                 }
705                 if ( !concise )
706                         ast_cli(fd,"%d users in that conference.\n",cnf->users);
707
708                 return RESULT_SUCCESS;
709         } else 
710                 return RESULT_SHOWUSAGE;
711         ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
712         admin_exec(NULL, cmdline);
713
714         return 0;
715 }
716
717 static char *complete_confcmd(const char *line, const char *word, int pos, int state)
718 {
719         static char *cmds[] = {"lock", "unlock", "mute", "unmute", "kick", "list", NULL};
720
721         int len = strlen(word);
722         int which = 0;
723         struct ast_conference *cnf = NULL;
724         struct ast_conf_user *usr = NULL;
725         char *confno = NULL;
726         char usrno[50] = "";
727         char *myline, *ret = NULL;
728         
729         if (pos == 1) {         /* Command */
730                 return ast_cli_complete(word, cmds, state);
731         } else if (pos == 2) {  /* Conference Number */
732                 AST_LIST_LOCK(&confs);
733                 AST_LIST_TRAVERSE(&confs, cnf, list) {
734                         if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
735                                 ret = cnf->confno;
736                                 break;
737                         }
738                 }
739                 ret = ast_strdup(ret); /* dup before releasing the lock */
740                 AST_LIST_UNLOCK(&confs);
741                 return ret;
742         } else if (pos == 3) {
743                 /* User Number || Conf Command option*/
744                 if (strstr(line, "mute") || strstr(line, "kick")) {
745                         if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
746                                 return strdup("all");
747                         which++;
748                         AST_LIST_LOCK(&confs);
749
750                         /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
751                         myline = ast_strdupa(line);
752                         if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
753                                 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
754                                         ;
755                         }
756                         
757                         AST_LIST_TRAVERSE(&confs, cnf, list) {
758                                 if (!strcmp(confno, cnf->confno))
759                                     break;
760                         }
761
762                         if (cnf) {
763                                 /* Search for the user */
764                                 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
765                                         snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
766                                         if (!strncasecmp(word, usrno, len) && ++which > state)
767                                                 break;
768                                 }
769                         }
770                         AST_LIST_UNLOCK(&confs);
771                         return usr ? strdup(usrno) : NULL;
772                 } else if ( strstr(line, "list") && ( 0 == state ) )
773                         return strdup("concise");
774         }
775
776         return NULL;
777 }
778         
779 static char conf_usage[] =
780 "Usage: meetme  (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
781 "       Executes a command for the conference or on a conferee\n";
782
783 static struct ast_cli_entry cli_conf = {
784         {"meetme", NULL, NULL }, conf_cmd,
785         "Execute a command on a conference or conferee", conf_usage, complete_confcmd};
786
787 static void conf_flush(int fd, struct ast_channel *chan)
788 {
789         int x;
790
791         /* read any frames that may be waiting on the channel
792            and throw them away
793         */
794         if (chan) {
795                 struct ast_frame *f;
796
797                 /* when no frames are available, this will wait
798                    for 1 millisecond maximum
799                 */
800                 while (ast_waitfor(chan, 1)) {
801                         f = ast_read(chan);
802                         if (f)
803                                 ast_frfree(f);
804                         else /* channel was hung up or something else happened */
805                                 break;
806                 }
807         }
808
809         /* flush any data sitting in the pseudo channel */
810         x = ZT_FLUSH_ALL;
811         if (ioctl(fd, ZT_FLUSH, &x))
812                 ast_log(LOG_WARNING, "Error flushing channel\n");
813
814 }
815
816 /* Remove the conference from the list and free it.
817    We assume that this was called while holding conflock. */
818 static int conf_free(struct ast_conference *conf)
819 {
820         int x;
821         
822         AST_LIST_REMOVE(&confs, conf, list);
823
824         if (conf->recording == MEETME_RECORD_ACTIVE) {
825                 conf->recording = MEETME_RECORD_TERMINATE;
826                 AST_LIST_UNLOCK(&confs);
827                 while (1) {
828                         AST_LIST_LOCK(&confs);
829                         if (conf->recording == MEETME_RECORD_OFF)
830                                 break;
831                         AST_LIST_UNLOCK(&confs);
832                 }
833         }
834
835         for (x=0;x<AST_FRAME_BITS;x++) {
836                 if (conf->transframe[x])
837                         ast_frfree(conf->transframe[x]);
838                 if (conf->transpath[x])
839                         ast_translator_free_path(conf->transpath[x]);
840         }
841         if (conf->origframe)
842                 ast_frfree(conf->origframe);
843         if (conf->lchan)
844                 ast_hangup(conf->lchan);
845         if (conf->chan)
846                 ast_hangup(conf->chan);
847         else
848                 close(conf->fd);
849         
850         free(conf);
851
852         return 0;
853 }
854
855 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
856 {
857         struct ast_conf_user *user = NULL;
858         struct ast_conf_user *usr = NULL;
859         int fd;
860         struct zt_confinfo ztc, ztc_empty;
861         struct ast_frame *f;
862         struct ast_channel *c;
863         struct ast_frame fr;
864         int outfd;
865         int ms;
866         int nfds;
867         int res;
868         int flags;
869         int retryzap;
870         int origfd;
871         int musiconhold = 0;
872         int firstpass = 0;
873         int lastmarked = 0;
874         int currentmarked = 0;
875         int ret = -1;
876         int x;
877         int menu_active = 0;
878         int using_pseudo = 0;
879         int duration=20;
880         int hr, min, sec;
881         time_t now;
882         struct ast_dsp *dsp=NULL;
883         struct ast_app *app;
884         const char *agifile;
885         const char *agifiledefault = "conf-background.agi";
886         char meetmesecs[30] = "";
887         char exitcontext[AST_MAX_CONTEXT] = "";
888         char recordingtmp[AST_MAX_EXTENSION] = "";
889         char members[10] = "";
890         int dtmf;
891         ZT_BUFFERINFO bi;
892         char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
893         char *buf = __buf + AST_FRIENDLY_OFFSET;
894
895         if (!(user = ast_calloc(1, sizeof(*user)))) {
896                 AST_LIST_LOCK(&confs);
897                 conf->refcount--;
898                 if (!conf->refcount){
899                         conf_free(conf);
900                 }
901                 AST_LIST_UNLOCK(&confs);
902                 return ret;
903         }
904
905         if (confflags & CONFFLAG_RECORDCONF) {
906                 if (!conf->recordingfilename) {
907                         conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
908                         if (!conf->recordingfilename) {
909                                 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
910                                 conf->recordingfilename = ast_strdupa(recordingtmp);
911                         }
912                         conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
913                         if (!conf->recordingformat) {
914                                 snprintf(recordingtmp, sizeof(recordingtmp), "wav");
915                                 conf->recordingformat = ast_strdupa(recordingtmp);
916                         }
917                         ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
918                                     conf->confno, conf->recordingfilename, conf->recordingformat);
919                 }
920         }
921
922         if ((conf->recording == MEETME_RECORD_OFF) && ((confflags & CONFFLAG_RECORDCONF) || (conf->lchan))) {
923                 pthread_attr_init(&conf->attr);
924                 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
925                 ast_pthread_create(&conf->recordthread, &conf->attr, recordthread, conf);
926         }
927
928         time(&user->jointime);
929
930         if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
931                 /* Sorry, but this confernce is locked! */      
932                 if (!ast_streamfile(chan, "conf-locked", chan->language))
933                         ast_waitstream(chan, "");
934                 goto outrun;
935         }
936
937         if (confflags & CONFFLAG_MARKEDUSER)
938                 conf->markedusers++;
939       
940         ast_mutex_lock(&conf->playlock);
941
942         if (AST_LIST_EMPTY(&conf->userlist))
943                 user->user_no = 1;
944         else
945                 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
946
947         AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
948
949         user->chan = chan;
950         user->userflags = confflags;
951         user->adminflags = 0;
952         user->talking = -1;
953         conf->users++;
954         /* Update table */
955         snprintf(members, sizeof(members), "%d", conf->users);
956         ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL);
957
958         ast_mutex_unlock(&conf->playlock);
959
960         if (confflags & CONFFLAG_EXIT_CONTEXT) {
961                 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) 
962                         ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
963                 else if (!ast_strlen_zero(chan->macrocontext)) 
964                         ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
965                 else
966                         ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
967         }
968
969         if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
970                 snprintf(user->namerecloc, sizeof(user->namerecloc),
971                          "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
972                          conf->confno, user->user_no);
973                 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
974                         res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
975                 else
976                         res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
977                 if (res == -1)
978                         goto outrun;
979         }
980
981         if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
982                 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
983                         if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
984                                 ast_waitstream(chan, "");
985                 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
986                         if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
987                                 ast_waitstream(chan, "");
988         }
989
990         if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
991                 int keepplaying = 1;
992
993                 if (conf->users == 2) { 
994                         if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
995                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
996                                 if (res > 0)
997                                         keepplaying=0;
998                                 else if (res == -1)
999                                         goto outrun;
1000                         }
1001                 } else { 
1002                         if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1003                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
1004                                 if (res > 0)
1005                                         keepplaying=0;
1006                                 else if (res == -1)
1007                                         goto outrun;
1008                         }
1009                         if (keepplaying) {
1010                                 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1011                                 if (res > 0)
1012                                         keepplaying=0;
1013                                 else if (res == -1)
1014                                         goto outrun;
1015                         }
1016                         if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1017                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
1018                                 if (res > 0)
1019                                         keepplaying=0;
1020                                 else if (res == -1) 
1021                                         goto outrun;
1022                         }
1023                 }
1024         }
1025
1026         ast_indicate(chan, -1);
1027
1028         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1029                 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1030                 goto outrun;
1031         }
1032
1033         if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1034                 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1035                 goto outrun;
1036         }
1037
1038         retryzap = strcasecmp(chan->tech->type, "Zap");
1039         user->zapchannel = !retryzap;
1040
1041  zapretry:
1042         origfd = chan->fds[0];
1043         if (retryzap) {
1044                 fd = open("/dev/zap/pseudo", O_RDWR);
1045                 if (fd < 0) {
1046                         ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1047                         goto outrun;
1048                 }
1049                 using_pseudo = 1;
1050                 /* Make non-blocking */
1051                 flags = fcntl(fd, F_GETFL);
1052                 if (flags < 0) {
1053                         ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1054                         close(fd);
1055                         goto outrun;
1056                 }
1057                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1058                         ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1059                         close(fd);
1060                         goto outrun;
1061                 }
1062                 /* Setup buffering information */
1063                 memset(&bi, 0, sizeof(bi));
1064                 bi.bufsize = CONF_SIZE/2;
1065                 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1066                 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1067                 bi.numbufs = audio_buffers;
1068                 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1069                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1070                         close(fd);
1071                         goto outrun;
1072                 }
1073                 x = 1;
1074                 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1075                         ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1076                         close(fd);
1077                         goto outrun;
1078                 }
1079                 nfds = 1;
1080         } else {
1081                 /* XXX Make sure we're not running on a pseudo channel XXX */
1082                 fd = chan->fds[0];
1083                 nfds = 0;
1084         }
1085         memset(&ztc, 0, sizeof(ztc));
1086         memset(&ztc_empty, 0, sizeof(ztc_empty));
1087         /* Check to see if we're in a conference... */
1088         ztc.chan = 0;   
1089         if (ioctl(fd, ZT_GETCONF, &ztc)) {
1090                 ast_log(LOG_WARNING, "Error getting conference\n");
1091                 close(fd);
1092                 goto outrun;
1093         }
1094         if (ztc.confmode) {
1095                 /* Whoa, already in a conference...  Retry... */
1096                 if (!retryzap) {
1097                         ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
1098                         retryzap = 1;
1099                         goto zapretry;
1100                 }
1101         }
1102         memset(&ztc, 0, sizeof(ztc));
1103         /* Add us to the conference */
1104         ztc.chan = 0;   
1105         ztc.confno = conf->zapconf;
1106
1107         ast_mutex_lock(&conf->playlock);
1108
1109         if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1110                 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1111                         if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1112                                 ast_waitstream(conf->chan, "");
1113                         if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1114                                 ast_waitstream(conf->chan, "");
1115                 }
1116         }
1117
1118         if (confflags & CONFFLAG_MONITOR)
1119                 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1120         else if (confflags & CONFFLAG_TALKER)
1121                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1122         else 
1123                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1124
1125         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1126                 ast_log(LOG_WARNING, "Error setting conference\n");
1127                 close(fd);
1128                 ast_mutex_unlock(&conf->playlock);
1129                 goto outrun;
1130         }
1131         ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1132
1133         manager_event(EVENT_FLAG_CALL, "MeetmeJoin", 
1134                       "Channel: %s\r\n"
1135                       "Uniqueid: %s\r\n"
1136                       "Meetme: %s\r\n"
1137                       "Usernum: %d\r\n",
1138                       chan->name, chan->uniqueid, conf->confno, user->user_no);
1139
1140         if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1141                 firstpass = 1;
1142                 if (!(confflags & CONFFLAG_QUIET))
1143                         if (!(confflags & CONFFLAG_WAITMARKED) || (conf->markedusers >= 1))
1144                                 conf_play(chan, conf, ENTER);
1145         }
1146
1147         ast_mutex_unlock(&conf->playlock);
1148
1149         conf_flush(fd, chan);
1150
1151         if (confflags & CONFFLAG_AGI) {
1152                 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1153                    or use default filename of conf-background.agi */
1154
1155                 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1156                 if (!agifile)
1157                         agifile = agifiledefault;
1158
1159                 if (user->zapchannel) {
1160                         /*  Set CONFMUTE mode on Zap channel to mute DTMF tones */
1161                         x = 1;
1162                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1163                 }
1164                 /* Find a pointer to the agi app and execute the script */
1165                 app = pbx_findapp("agi");
1166                 if (app) {
1167                         char *s = ast_strdupa(agifile);
1168                         ret = pbx_exec(chan, app, s);
1169                 } else {
1170                         ast_log(LOG_WARNING, "Could not find application (agi)\n");
1171                         ret = -2;
1172                 }
1173                 if (user->zapchannel) {
1174                         /*  Remove CONFMUTE mode on Zap channel */
1175                         x = 0;
1176                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1177                 }
1178         } else {
1179                 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1180                         /*  Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1181                         x = 1;
1182                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1183                 }       
1184                 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER) && !(dsp = ast_dsp_new())) {
1185                         ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1186                         res = -1;
1187                 }
1188                 for(;;) {
1189                         int menu_was_active = 0;
1190
1191                         outfd = -1;
1192                         ms = -1;
1193                         
1194                         /* if we have just exited from the menu, and the user had a channel-driver
1195                            volume adjustment, restore it
1196                         */
1197                         if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
1198                                 set_talk_volume(user, user->listen.desired);
1199
1200                         menu_was_active = menu_active;
1201
1202                         currentmarked = conf->markedusers;
1203                         if (!(confflags & CONFFLAG_QUIET) &&
1204                             (confflags & CONFFLAG_MARKEDUSER) &&
1205                             (confflags & CONFFLAG_WAITMARKED) &&
1206                             lastmarked == 0) {
1207                                 if (currentmarked == 1 && conf->users > 1) {
1208                                         ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1209                                         if (conf->users - 1 == 1) {
1210                                                 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
1211                                                         ast_waitstream(chan, "");
1212                                         } else {
1213                                                 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
1214                                                         ast_waitstream(chan, "");
1215                                         }
1216                                 }
1217                                 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
1218                                         if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1219                                                 ast_waitstream(chan, "");
1220                         }
1221
1222                         c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
1223                         
1224                         
1225                         /* Update the struct with the actual confflags */
1226                         user->userflags = confflags;
1227                         
1228                         if (confflags & CONFFLAG_WAITMARKED) {
1229                                 if(currentmarked == 0) {
1230                                         if (lastmarked != 0) {
1231                                                 if (!(confflags & CONFFLAG_QUIET))
1232                                                         if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
1233                                                                 ast_waitstream(chan, "");
1234                                                 if(confflags & CONFFLAG_MARKEDEXIT)
1235                                                         break;
1236                                                 else {
1237                                                         ztc.confmode = ZT_CONF_CONF;
1238                                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1239                                                                 ast_log(LOG_WARNING, "Error setting conference\n");
1240                                                                 close(fd);
1241                                                                 goto outrun;
1242                                                         }
1243                                                 }
1244                                         }
1245                                         if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
1246                                                 ast_moh_start(chan, NULL);
1247                                                 musiconhold = 1;
1248                                         } else {
1249                                                 ztc.confmode = ZT_CONF_CONF;
1250                                                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1251                                                         ast_log(LOG_WARNING, "Error setting conference\n");
1252                                                         close(fd);
1253                                                         goto outrun;
1254                                                 }
1255                                         }
1256                                 } else if(currentmarked >= 1 && lastmarked == 0) {
1257                                         if (confflags & CONFFLAG_MONITOR)
1258                                                 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1259                                         else if (confflags & CONFFLAG_TALKER)
1260                                                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1261                                         else
1262                                                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1263                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1264                                                 ast_log(LOG_WARNING, "Error setting conference\n");
1265                                                 close(fd);
1266                                                 goto outrun;
1267                                         }
1268                                         if (musiconhold && (confflags & CONFFLAG_MOH)) {
1269                                                 ast_moh_stop(chan);
1270                                                 musiconhold = 0;
1271                                         }
1272                                         if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
1273                                                 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
1274                                                         ast_waitstream(chan, "");
1275                                                 conf_play(chan, conf, ENTER);
1276                                         }
1277                                 }
1278                         }
1279
1280                         /* trying to add moh for single person conf */
1281                         if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
1282                                 if (conf->users == 1) {
1283                                         if (musiconhold == 0) {
1284                                                 ast_moh_start(chan, NULL);
1285                                                 musiconhold = 1;
1286                                         } 
1287                                 } else {
1288                                         if (musiconhold) {
1289                                                 ast_moh_stop(chan);
1290                                                 musiconhold = 0;
1291                                         }
1292                                 }
1293                         }
1294                         
1295                         /* Leave if the last marked user left */
1296                         if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
1297                                 ret = -1;
1298                                 break;
1299                         }
1300         
1301                         /* Check if the admin changed my modes */
1302                         if (user->adminflags) {                 
1303                                 /* Set the new modes */
1304                                 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
1305                                         ztc.confmode ^= ZT_CONF_TALKER;
1306                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1307                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1308                                                 ret = -1;
1309                                                 break;
1310                                         }
1311                                 }
1312                                 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1313                                         ztc.confmode |= ZT_CONF_TALKER;
1314                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1315                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1316                                                 ret = -1;
1317                                                 break;
1318                                         }
1319                                 }
1320                                 if (user->adminflags & ADMINFLAG_KICKME) {
1321                                         /* You have been kicked. */
1322                                         if (!ast_streamfile(chan, "conf-kicked", chan->language))
1323                                                 ast_waitstream(chan, "");
1324                                         ret = 0;
1325                                         break;
1326                                 }
1327                         } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1328                                 ztc.confmode |= ZT_CONF_TALKER;
1329                                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1330                                         ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1331                                         ret = -1;
1332                                         break;
1333                                 }
1334                         }
1335
1336                         if (c) {
1337                                 if (c->fds[0] != origfd) {
1338                                         if (using_pseudo) {
1339                                                 /* Kill old pseudo */
1340                                                 close(fd);
1341                                                 using_pseudo = 0;
1342                                         }
1343                                         ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
1344                                         retryzap = strcasecmp(c->tech->type, "Zap");
1345                                         user->zapchannel = !retryzap;
1346                                         goto zapretry;
1347                                 }
1348                                 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & ADMINFLAG_MUTED))
1349                                         f = ast_read_noaudio(c);
1350                                 else
1351                                         f = ast_read(c);
1352                                 if (!f)
1353                                         break;
1354                                 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1355                                         if (user->talk.actual)
1356                                                 ast_frame_adjust_volume(f, user->talk.actual);
1357
1358                                         if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER)) {
1359                                                 int totalsilence;
1360
1361                                                 if (user->talking == -1)
1362                                                         user->talking = 0;
1363
1364                                                 res = ast_dsp_silence(dsp, f, &totalsilence);
1365                                                 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1366                                                         user->talking = 1;
1367                                                         if (confflags & CONFFLAG_MONITORTALKER)
1368                                                                 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1369                                                                       "Channel: %s\r\n"
1370                                                                       "Uniqueid: %s\r\n"
1371                                                                       "Meetme: %s\r\n"
1372                                                                       "Usernum: %d\r\n",
1373                                                                       chan->name, chan->uniqueid, conf->confno, user->user_no);
1374                                                 }
1375                                                 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1376                                                         user->talking = 0;
1377                                                         if (confflags & CONFFLAG_MONITORTALKER)
1378                                                                 manager_event(EVENT_FLAG_CALL, "MeetmeStopTalking",
1379                                                                       "Channel: %s\r\n"
1380                                                                       "Uniqueid: %s\r\n"
1381                                                                       "Meetme: %s\r\n"
1382                                                                       "Usernum: %d\r\n",
1383                                                                       chan->name, chan->uniqueid, conf->confno, user->user_no);
1384                                                 }
1385                                         }
1386                                         if (using_pseudo) {
1387                                                 /* Absolutely do _not_ use careful_write here...
1388                                                    it is important that we read data from the channel
1389                                                    as fast as it arrives, and feed it into the conference.
1390                                                    The buffering in the pseudo channel will take care of any
1391                                                    timing differences, unless they are so drastic as to lose
1392                                                    audio frames (in which case carefully writing would only
1393                                                    have delayed the audio even further).
1394                                                 */
1395                                                 /* As it turns out, we do want to use careful write.  We just
1396                                                    don't want to block, but we do want to at least *try*
1397                                                    to write out all the samples.
1398                                                  */
1399                                                 if (user->talking || !(confflags & CONFFLAG_OPTIMIZETALKER))
1400                                                         careful_write(fd, f->data, f->datalen, 0);
1401                                         }
1402                                 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
1403                                         char tmp[2];
1404
1405                                         tmp[0] = f->subclass;
1406                                         tmp[1] = '\0';
1407                                         if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
1408                                                 ast_log(LOG_DEBUG, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
1409                                                 ret = 0;
1410                                                 break;
1411                                         } else if (option_debug > 1)
1412                                                 ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
1413                                 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
1414                                         ret = 0;
1415                                         break;
1416                                 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
1417                                         if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
1418                                                 ast_log(LOG_WARNING, "Error setting conference\n");
1419                                                 close(fd);
1420                                                 goto outrun;
1421                                         }
1422
1423                                         /* if we are entering the menu, and the user has a channel-driver
1424                                            volume adjustment, clear it
1425                                         */
1426                                         if (!menu_active && user->talk.desired && !user->talk.actual)
1427                                                 set_talk_volume(user, 0);
1428
1429                                         if (musiconhold) {
1430                                                 ast_moh_stop(chan);
1431                                         }
1432                                         if ((confflags & CONFFLAG_ADMIN)) {
1433                                                 /* Admin menu */
1434                                                 if (!menu_active) {
1435                                                         menu_active = 1;
1436                                                         /* Record this sound! */
1437                                                         if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
1438                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1439                                                                 ast_stopstream(chan);
1440                                                         } else 
1441                                                                 dtmf = 0;
1442                                                 } else 
1443                                                         dtmf = f->subclass;
1444                                                 if (dtmf) {
1445                                                         switch(dtmf) {
1446                                                         case '1': /* Un/Mute */
1447                                                                 menu_active = 0;
1448                                                                 if (ztc.confmode & ZT_CONF_TALKER) {
1449                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1450                                                                         confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1451                                                                 } else {
1452                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1453                                                                         confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1454                                                                 }
1455                                                                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1456                                                                         ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1457                                                                         ret = -1;
1458                                                                         break;
1459                                                                 }
1460                                                                 if (ztc.confmode & ZT_CONF_TALKER) {
1461                                                                         if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1462                                                                                 ast_waitstream(chan, "");
1463                                                                 } else {
1464                                                                         if (!ast_streamfile(chan, "conf-muted", chan->language))
1465                                                                                 ast_waitstream(chan, "");
1466                                                                 }
1467                                                                 break;
1468                                                         case '2': /* Un/Lock the Conference */
1469                                                                 menu_active = 0;
1470                                                                 if (conf->locked) {
1471                                                                         conf->locked = 0;
1472                                                                         if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1473                                                                                 ast_waitstream(chan, "");
1474                                                                 } else {
1475                                                                         conf->locked = 1;
1476                                                                         if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1477                                                                                 ast_waitstream(chan, "");
1478                                                                 }
1479                                                                 break;
1480                                                         case '3': /* Eject last user */
1481                                                                 menu_active = 0;
1482                                                                 usr = AST_LIST_LAST(&conf->userlist);
1483                                                                 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1484                                                                         if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1485                                                                                 ast_waitstream(chan, "");
1486                                                                 } else 
1487                                                                         usr->adminflags |= ADMINFLAG_KICKME;
1488                                                                 ast_stopstream(chan);
1489                                                                 break;  
1490                                                         case '4':
1491                                                                 tweak_listen_volume(user, VOL_DOWN);
1492                                                                 break;
1493                                                         case '6':
1494                                                                 tweak_listen_volume(user, VOL_UP);
1495                                                                 break;
1496                                                         case '7':
1497                                                                 tweak_talk_volume(user, VOL_DOWN);
1498                                                                 break;
1499                                                         case '8':
1500                                                                 menu_active = 0;
1501                                                                 break;
1502                                                         case '9':
1503                                                                 tweak_talk_volume(user, VOL_UP);
1504                                                                 break;
1505                                                         default:
1506                                                                 menu_active = 0;
1507                                                                 /* Play an error message! */
1508                                                                 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1509                                                                         ast_waitstream(chan, "");
1510                                                                 break;
1511                                                         }
1512                                                 }
1513                                         } else {
1514                                                 /* User menu */
1515                                                 if (!menu_active) {
1516                                                         menu_active = 1;
1517                                                         if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
1518                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1519                                                                 ast_stopstream(chan);
1520                                                         } else
1521                                                                 dtmf = 0;
1522                                                 } else 
1523                                                         dtmf = f->subclass;
1524                                                 if (dtmf) {
1525                                                         switch(dtmf) {
1526                                                         case '1': /* Un/Mute */
1527                                                                 menu_active = 0;
1528                                                                 if (ztc.confmode & ZT_CONF_TALKER) {
1529                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1530                                                                         confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1531                                                                 } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
1532                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1533                                                                         confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1534                                                                 }
1535                                                                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1536                                                                         ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1537                                                                         ret = -1;
1538                                                                         break;
1539                                                                 }
1540                                                                 if (ztc.confmode & ZT_CONF_TALKER) {
1541                                                                         if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1542                                                                                 ast_waitstream(chan, "");
1543                                                                 } else {
1544                                                                         if (!ast_streamfile(chan, "conf-muted", chan->language))
1545                                                                                 ast_waitstream(chan, "");
1546                                                                 }
1547                                                                 break;
1548                                                         case '4':
1549                                                                 tweak_listen_volume(user, VOL_DOWN);
1550                                                                 break;
1551                                                         case '6':
1552                                                                 tweak_listen_volume(user, VOL_UP);
1553                                                                 break;
1554                                                         case '7':
1555                                                                 tweak_talk_volume(user, VOL_DOWN);
1556                                                                 break;
1557                                                         case '8':
1558                                                                 menu_active = 0;
1559                                                                 break;
1560                                                         case '9':
1561                                                                 tweak_talk_volume(user, VOL_UP);
1562                                                                 break;
1563                                                         default:
1564                                                                 menu_active = 0;
1565                                                                 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1566                                                                         ast_waitstream(chan, "");
1567                                                                 break;
1568                                                         }
1569                                                 }
1570                                         }
1571                                         if (musiconhold)
1572                                                 ast_moh_start(chan, NULL);
1573
1574                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1575                                                 ast_log(LOG_WARNING, "Error setting conference\n");
1576                                                 close(fd);
1577                                                 AST_LIST_UNLOCK(&confs);
1578                                                 goto outrun;
1579                                         }
1580
1581                                         conf_flush(fd, chan);
1582                                 } else if (option_debug) {
1583                                         ast_log(LOG_DEBUG,
1584                                                 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
1585                                                 chan->name, f->frametype, f->subclass);
1586                                 }
1587                                 ast_frfree(f);
1588                         } else if (outfd > -1) {
1589                                 res = read(outfd, buf, CONF_SIZE);
1590                                 if (res > 0) {
1591                                         memset(&fr, 0, sizeof(fr));
1592                                         fr.frametype = AST_FRAME_VOICE;
1593                                         fr.subclass = AST_FORMAT_SLINEAR;
1594                                         fr.datalen = res;
1595                                         fr.samples = res/2;
1596                                         fr.data = buf;
1597                                         fr.offset = AST_FRIENDLY_OFFSET;
1598                                         if (!user->listen.actual && 
1599                                                 ((confflags & CONFFLAG_MONITOR) || 
1600                                                  (user->adminflags & ADMINFLAG_MUTED) ||
1601                                                  (!user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
1602                                                  )) {
1603                                                 int index;
1604                                                 for (index=0;index<AST_FRAME_BITS;index++)
1605                                                         if (chan->rawwriteformat & (1 << index))
1606                                                                 break;
1607                                                 if (index >= AST_FRAME_BITS)
1608                                                         goto bailoutandtrynormal;
1609                                                 ast_mutex_lock(&conf->listenlock);
1610                                                 if (!conf->transframe[index]) {
1611                                                         if (conf->origframe) {
1612                                                                 if (!conf->transpath[index])
1613                                                                         conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
1614                                                                 if (conf->transpath[index]) {
1615                                                                         conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
1616                                                                         if (!conf->transframe[index])
1617                                                                                 conf->transframe[index] = &ast_null_frame;
1618                                                                 }
1619                                                         }
1620                                                 }
1621                                                 if (conf->transframe[index]) {
1622                                                         if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
1623                                                                 if (ast_write(chan, conf->transframe[index]))
1624                                                                         ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1625                                                         }
1626                                                 } else {
1627                                                         ast_mutex_unlock(&conf->listenlock);
1628                                                         goto bailoutandtrynormal;
1629                                                 }
1630                                                 ast_mutex_unlock(&conf->listenlock);
1631                                         } else {
1632 bailoutandtrynormal:                                    
1633                                                 if (user->listen.actual)
1634                                                         ast_frame_adjust_volume(&fr, user->listen.actual);
1635                                                 if (ast_write(chan, &fr) < 0) {
1636                                                         ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1637                                                 }
1638                                         }
1639                                 } else 
1640                                         ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1641                         }
1642                         lastmarked = currentmarked;
1643                 }
1644         }
1645
1646         if (musiconhold)
1647                 ast_moh_stop(chan);
1648         
1649         if (using_pseudo)
1650                 close(fd);
1651         else {
1652                 /* Take out of conference */
1653                 ztc.chan = 0;   
1654                 ztc.confno = 0;
1655                 ztc.confmode = 0;
1656                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1657                         ast_log(LOG_WARNING, "Error setting conference\n");
1658                 }
1659         }
1660
1661         reset_volumes(user);
1662
1663         AST_LIST_LOCK(&confs);
1664         if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1665                 conf_play(chan, conf, LEAVE);
1666
1667         if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1668                 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
1669                         if ((conf->chan) && (conf->users > 1)) {
1670                                 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1671                                         ast_waitstream(conf->chan, "");
1672                                 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1673                                         ast_waitstream(conf->chan, "");
1674                         }
1675                         ast_filedelete(user->namerecloc, NULL);
1676                 }
1677         }
1678         AST_LIST_UNLOCK(&confs);
1679
1680  outrun:
1681         AST_LIST_LOCK(&confs);
1682
1683         if (dsp)
1684                 ast_dsp_free(dsp);
1685         
1686         if (user->user_no) { /* Only cleanup users who really joined! */
1687                 now = time(NULL);
1688                 hr = (now - user->jointime) / 3600;
1689                 min = ((now - user->jointime) % 3600) / 60;
1690                 sec = (now - user->jointime) % 60;
1691
1692                 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
1693                         "Channel: %s\r\n"
1694                         "Uniqueid: %s\r\n"
1695                         "Meetme: %s\r\n"
1696                         "Usernum: %d\r\n"
1697                         "CallerIDnum: %s\r\n"
1698                         "CallerIDname: %s\r\n"
1699                         "Duration: %ld\r\n",
1700                         chan->name, chan->uniqueid, conf->confno, 
1701                         user->user_no,
1702                         S_OR(user->chan->cid.cid_num, "<unknown>"),
1703                         S_OR(user->chan->cid.cid_name, "<unknown>"),
1704                         (now - user->jointime));
1705
1706                 conf->users--;
1707                 conf->refcount--;
1708                 /* Update table */
1709                 snprintf(members, sizeof(members), "%d", conf->users);
1710                 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1711                 if (confflags & CONFFLAG_MARKEDUSER) 
1712                         conf->markedusers--;
1713                 if (AST_LIST_EMPTY(&conf->userlist)) {
1714                         /* close this one when no more users and no references*/
1715                         if (!conf->refcount)
1716                                 conf_free(conf);
1717                 } else {
1718                         AST_LIST_REMOVE(&conf->userlist, user, list);
1719                 }
1720                 /* Return the number of seconds the user was in the conf */
1721                 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
1722                 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1723         }
1724         free(user);
1725         AST_LIST_UNLOCK(&confs);
1726
1727         return ret;
1728 }
1729
1730 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
1731                                                  char *dynamic_pin, int refcount, struct ast_flags *confflags)
1732 {
1733         struct ast_variable *var;
1734         struct ast_conference *cnf;
1735
1736         /* Check first in the conference list */
1737         AST_LIST_LOCK(&confs);
1738         AST_LIST_TRAVERSE(&confs, cnf, list) {
1739                 if (!strcmp(confno, cnf->confno)) 
1740                         break;
1741         }
1742         if (cnf){
1743                 cnf->refcount += refcount;
1744         }
1745         AST_LIST_UNLOCK(&confs);
1746
1747         if (!cnf) {
1748                 char *pin = NULL, *pinadmin = NULL; /* For temp use */
1749
1750                 cnf = ast_calloc(1, sizeof(struct ast_conference));
1751                 if (!cnf) {
1752                         ast_log(LOG_ERROR, "Out of memory\n");
1753                         return NULL;
1754                 }
1755
1756                 var = ast_load_realtime("meetme", "confno", confno, NULL);
1757                 while (var) {
1758                         if (!strcasecmp(var->name, "confno")) {
1759                                 ast_copy_string(cnf->confno, var->value, sizeof(cnf->confno));
1760                         } else if (!strcasecmp(var->name, "pin")) {
1761                                 pin = ast_strdupa(var->value);
1762                         } else if (!strcasecmp(var->name, "adminpin")) {
1763                                 pinadmin = ast_strdupa(var->value);
1764                         }
1765                         var = var->next;
1766                 }
1767                 ast_variables_destroy(var);
1768
1769                 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount);
1770         }
1771
1772         if (cnf) {
1773                 if (confflags && !cnf->chan &&
1774                     !ast_test_flag(confflags, CONFFLAG_QUIET) &&
1775                     ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
1776                         ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
1777                         ast_clear_flag(confflags, CONFFLAG_INTROUSER);
1778                 }
1779                 
1780                 if (confflags && !cnf->chan &&
1781                     ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
1782                         ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
1783                         ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
1784                 }
1785         }
1786
1787         return cnf;
1788 }
1789
1790
1791 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
1792                                         char *dynamic_pin, int refcount, struct ast_flags *confflags)
1793 {
1794         struct ast_config *cfg;
1795         struct ast_variable *var;
1796         struct ast_conference *cnf;
1797         char *parse;
1798         AST_DECLARE_APP_ARGS(args,
1799                 AST_APP_ARG(confno);
1800                 AST_APP_ARG(pin);
1801                 AST_APP_ARG(pinadmin);
1802         );
1803
1804         /* Check first in the conference list */
1805         AST_LIST_LOCK(&confs);
1806         AST_LIST_TRAVERSE(&confs, cnf, list) {
1807                 if (!strcmp(confno, cnf->confno)) 
1808                         break;
1809         }
1810         if (cnf){
1811                 cnf->refcount += refcount;
1812         }
1813         AST_LIST_UNLOCK(&confs);
1814
1815         if (!cnf) {
1816                 if (dynamic) {
1817                         /* No need to parse meetme.conf */
1818                         ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1819                         if (dynamic_pin) {
1820                                 if (dynamic_pin[0] == 'q') {
1821                                         /* Query the user to enter a PIN */
1822                                         if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0) < 0)
1823                                                 return NULL;
1824                                 }
1825                                 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount);
1826                         } else {
1827                                 cnf = build_conf(confno, "", "", make, dynamic, refcount);
1828                         }
1829                 } else {
1830                         /* Check the config */
1831                         cfg = ast_config_load(CONFIG_FILE_NAME);
1832                         if (!cfg) {
1833                                 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
1834                                 return NULL;
1835                         }
1836                         for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
1837                                 if (strcasecmp(var->name, "conf"))
1838                                         continue;
1839                                 
1840                                 if (!(parse = ast_strdupa(var->value)))
1841                                         return NULL;
1842                                 
1843                                 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
1844                                 if (!strcasecmp(args.confno, confno)) {
1845                                         /* Bingo it's a valid conference */
1846                                         cnf = build_conf(args.confno,
1847                                                         S_OR(args.pin, ""),
1848                                                         S_OR(args.pinadmin, ""),
1849                                                         make, dynamic, refcount);
1850                                         break;
1851                                 }
1852                         }
1853                         if (!var) {
1854                                 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1855                         }
1856                         ast_config_destroy(cfg);
1857                 }
1858         } else if (dynamic_pin) {
1859                 /* Correct for the user selecting 'D' instead of 'd' to have
1860                    someone join into a conference that has already been created
1861                    with a pin. */
1862                 if (dynamic_pin[0] == 'q')
1863                         dynamic_pin[0] = '\0';
1864         }
1865
1866         if (cnf) {
1867                 if (confflags && !cnf->chan &&
1868                     !ast_test_flag(confflags, CONFFLAG_QUIET) &&
1869                     ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
1870                         ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
1871                         ast_clear_flag(confflags, CONFFLAG_INTROUSER);
1872                 }
1873                 
1874                 if (confflags && !cnf->chan &&
1875                     ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
1876                         ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
1877                         ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
1878                 }
1879         }
1880
1881         return cnf;
1882 }
1883
1884 /*! \brief The MeetmeCount application */
1885 static int count_exec(struct ast_channel *chan, void *data)
1886 {
1887         struct localuser *u;
1888         int res = 0;
1889         struct ast_conference *conf;
1890         int count;
1891         char *localdata;
1892         char val[80] = "0"; 
1893         AST_DECLARE_APP_ARGS(args,
1894                 AST_APP_ARG(confno);
1895                 AST_APP_ARG(varname);
1896         );
1897
1898         if (ast_strlen_zero(data)) {
1899                 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1900                 return -1;
1901         }
1902
1903         LOCAL_USER_ADD(u);
1904         
1905         if (!(localdata = ast_strdupa(data))) {
1906                 LOCAL_USER_REMOVE(u);
1907                 return -1;
1908         }
1909
1910         AST_STANDARD_APP_ARGS(args, localdata);
1911         
1912         conf = find_conf(chan, args.confno, 0, 0, NULL, 0, NULL);
1913
1914         if (conf)
1915                 count = conf->users;
1916         else
1917                 count = 0;
1918
1919         if (!ast_strlen_zero(args.varname)){
1920                 /* have var so load it and exit */
1921                 snprintf(val, sizeof(val), "%d",count);
1922                 pbx_builtin_setvar_helper(chan, args.varname, val);
1923         } else {
1924                 if (chan->_state != AST_STATE_UP)
1925                         ast_answer(chan);
1926                 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1927         }
1928         LOCAL_USER_REMOVE(u);
1929
1930         return res;
1931 }
1932
1933 /*! \brief The meetme() application */
1934 static int conf_exec(struct ast_channel *chan, void *data)
1935 {
1936         int res=-1;
1937         struct localuser *u;
1938         char confno[AST_MAX_EXTENSION] = "";
1939         int allowretry = 0;
1940         int retrycnt = 0;
1941         struct ast_conference *cnf;
1942         struct ast_flags confflags = {0};
1943         int dynamic = 0;
1944         int empty = 0, empty_no_pin = 0;
1945         int always_prompt = 0;
1946         char *notdata, *info, the_pin[AST_MAX_EXTENSION] = "";
1947         AST_DECLARE_APP_ARGS(args,
1948                 AST_APP_ARG(confno);
1949                 AST_APP_ARG(options);
1950                 AST_APP_ARG(pin);
1951         );
1952
1953         LOCAL_USER_ADD(u);
1954
1955         if (ast_strlen_zero(data)) {
1956                 allowretry = 1;
1957                 notdata = "";
1958         } else {
1959                 notdata = data;
1960         }
1961         
1962         if (chan->_state != AST_STATE_UP)
1963                 ast_answer(chan);
1964
1965         info = ast_strdupa(notdata);
1966
1967         AST_STANDARD_APP_ARGS(args, info);      
1968
1969         if (args.confno) {
1970                 ast_copy_string(confno, args.confno, sizeof(confno));
1971                 if (ast_strlen_zero(confno)) {
1972                         allowretry = 1;
1973                 }
1974         }
1975         
1976         if (args.pin)
1977                 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
1978
1979         if (args.options) {
1980                 ast_app_parse_options(meetme_opts, &confflags, NULL, args.options);
1981                 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
1982                 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
1983                         strcpy(the_pin, "q");
1984
1985                 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
1986                 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
1987                 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
1988         }
1989
1990         do {
1991                 if (retrycnt > 3)
1992                         allowretry = 0;
1993                 if (empty) {
1994                         int i, map[1024] = { 0, };
1995                         struct ast_config *cfg;
1996                         struct ast_variable *var;
1997                         int confno_int;
1998
1999                         AST_LIST_LOCK(&confs);
2000                         AST_LIST_TRAVERSE(&confs, cnf, list) {
2001                                 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
2002                                         /* Disqualify in use conference */
2003                                         if (confno_int >= 0 && confno_int < 1024)
2004                                                 map[confno_int]++;
2005                                 }
2006                         }
2007                         AST_LIST_UNLOCK(&confs);
2008
2009                         /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2010                         if ((empty_no_pin) || (!dynamic)) {
2011                                 cfg = ast_config_load(CONFIG_FILE_NAME);
2012                                 if (cfg) {
2013                                         var = ast_variable_browse(cfg, "rooms");
2014                                         while (var) {
2015                                                 if (!strcasecmp(var->name, "conf")) {
2016                                                         char *stringp = ast_strdupa(var->value);
2017                                                         if (stringp) {
2018                                                                 char *confno_tmp = strsep(&stringp, "|,");
2019                                                                 int found = 0;
2020                                                                 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
2021                                                                         if ((confno_int >= 0) && (confno_int < 1024)) {
2022                                                                                 if (stringp && empty_no_pin) {
2023                                                                                         map[confno_int]++;
2024                                                                                 }
2025                                                                         }
2026                                                                 }
2027                                                                 if (!dynamic) {
2028                                                                         /* For static:  run through the list and see if this conference is empty */
2029                                                                         AST_LIST_LOCK(&confs);
2030                                                                         AST_LIST_TRAVERSE(&confs, cnf, list) {
2031                                                                                 if (!strcmp(confno_tmp, cnf->confno)) {
2032                                                                                         /* The conference exists, therefore it's not empty */
2033                                                                                         found = 1;
2034                                                                                         break;
2035                                                                                 }
2036                                                                         }
2037                                                                         AST_LIST_UNLOCK(&confs);
2038                                                                         if (!found) {
2039                                                                                 /* At this point, we have a confno_tmp (static conference) that is empty */
2040                                                                                 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2041                                                                                         /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
2042                                                                                          * Case 2:  empty_no_pin and pin is blank (but not NULL)
2043                                                                                          * Case 3:  not empty_no_pin
2044                                                                                          */
2045                                                                                         ast_copy_string(confno, confno_tmp, sizeof(confno));
2046                                                                                         break;
2047                                                                                         /* XXX the map is not complete (but we do have a confno) */
2048                                                                                 }
2049                                                                         }
2050                                                                 }
2051                                                         }
2052                                                 }
2053                                                 var = var->next;
2054                                         }
2055                                         ast_config_destroy(cfg);
2056                                 }
2057                         }
2058
2059                         /* Select first conference number not in use */
2060                         if (ast_strlen_zero(confno) && dynamic) {
2061                                 for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
2062                                         if (!map[i]) {
2063                                                 snprintf(confno, sizeof(confno), "%d", i);
2064                                                 break;
2065                                         }
2066                                 }
2067                         }
2068
2069                         /* Not found? */
2070                         if (ast_strlen_zero(confno)) {
2071                                 res = ast_streamfile(chan, "conf-noempty", chan->language);
2072                                 if (!res)
2073                                         ast_waitstream(chan, "");
2074                         } else {
2075                                 if (sscanf(confno, "%d", &confno_int) == 1) {
2076                                         res = ast_streamfile(chan, "conf-enteringno", chan->language);
2077                                         if (!res) {
2078                                                 ast_waitstream(chan, "");
2079                                                 res = ast_say_digits(chan, confno_int, "", chan->language);
2080                                         }
2081                                 } else {
2082                                         ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2083                                 }
2084                         }
2085                 }
2086
2087                 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2088                         /* Prompt user for conference number */
2089                         res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2090                         if (res < 0) {
2091                                 /* Don't try to validate when we catch an error */
2092                                 confno[0] = '\0';
2093                                 allowretry = 0;
2094                                 break;
2095                         }
2096                 }
2097                 if (!ast_strlen_zero(confno)) {
2098                         /* Check the validity of the conference */
2099                         cnf = find_conf(chan, confno, 1, dynamic, the_pin, 1, &confflags);
2100                         if (!cnf)
2101                                 cnf = find_conf_realtime(chan, confno, 1, dynamic, the_pin, 1, &confflags);
2102
2103                         if (!cnf) {
2104                                 res = ast_streamfile(chan, "conf-invalid", chan->language);
2105                                 if (!res)
2106                                         ast_waitstream(chan, "");
2107                                 res = -1;
2108                                 if (allowretry)
2109                                         confno[0] = '\0';
2110                         } else {
2111                                 if ((!ast_strlen_zero(cnf->pin) &&
2112                                      !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2113                                     (!ast_strlen_zero(cnf->pinadmin) &&
2114                                      ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2115                                         char pin[AST_MAX_EXTENSION]="";
2116                                         int j;
2117
2118                                         /* Allow the pin to be retried up to 3 times */
2119                                         for (j = 0; j < 3; j++) {
2120                                                 if (*the_pin && (always_prompt == 0)) {
2121                                                         ast_copy_string(pin, the_pin, sizeof(pin));
2122                                                         res = 0;
2123                                                 } else {
2124                                                         /* Prompt user for pin if pin is required */
2125                                                         res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2126                                                 }
2127                                                 if (res >= 0) {
2128                                                         if (!strcasecmp(pin, cnf->pin) ||
2129                                                             (!ast_strlen_zero(cnf->pinadmin) &&
2130                                                              !strcasecmp(pin, cnf->pinadmin))) {
2131                                                                 /* Pin correct */
2132                                                                 allowretry = 0;
2133                                                                 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) 
2134                                                                         ast_set_flag(&confflags, CONFFLAG_ADMIN);
2135                                                                 /* Run the conference */
2136                                                                 res = conf_run(chan, cnf, confflags.flags);
2137                                                                 break;
2138                                                         } else {
2139                                                                 /* Pin invalid */
2140                                                                 if (!ast_streamfile(chan, "conf-invalidpin", chan->language))
2141                                                                         res = ast_waitstream(chan, AST_DIGIT_ANY);
2142                                                                 else {
2143                                                                         ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
2144                                                                         break;
2145                                                                 }
2146                                                                 if (res < 0) {
2147                                                                         AST_LIST_LOCK(&confs);
2148                                                                         cnf->refcount--;
2149                                                                         if (!cnf->refcount){
2150                                                                                 conf_free(cnf);
2151                                                                         }
2152                                                                         AST_LIST_UNLOCK(&confs);
2153                                                                         break;
2154                                                                 }
2155                                                                 pin[0] = res;
2156                                                                 pin[1] = '\0';
2157                                                                 res = -1;
2158                                                                 if (allowretry)
2159                                                                         confno[0] = '\0';
2160                                                         }
2161                                                 } else {
2162                                                         /* failed when getting the pin */
2163                                                         res = -1;
2164                                                         allowretry = 0;
2165                                                         /* see if we need to get rid of the conference */
2166                                                         AST_LIST_LOCK(&confs);
2167                                                         cnf->refcount--;
2168                                                         if (!cnf->refcount) {
2169                                                                 conf_free(cnf);
2170                                                         }
2171                                                         AST_LIST_UNLOCK(&confs);
2172                                                         break;
2173                                                 }
2174
2175                                                 /* Don't retry pin with a static pin */
2176                                                 if (*the_pin && (always_prompt==0)) {
2177                                                         break;
2178                                                 }
2179                                         }
2180                                 } else {
2181                                         /* No pin required */
2182                                         allowretry = 0;
2183
2184                                         /* Run the conference */
2185                                         res = conf_run(chan, cnf, confflags.flags);
2186                                 }
2187                         }
2188                 }
2189         } while (allowretry);
2190         
2191         LOCAL_USER_REMOVE(u);
2192         
2193         return res;
2194 }
2195
2196 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident) 
2197 {
2198         struct ast_conf_user *user = NULL;
2199         int cid;
2200         
2201         sscanf(callerident, "%i", &cid);
2202         if (conf && callerident) {
2203                 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2204                         if (cid == user->user_no)
2205                                 return user;
2206                 }
2207         }
2208         return NULL;
2209 }
2210
2211 /*! \brief The MeetMeadmin application */
2212 /* MeetMeAdmin(confno, command, caller) */
2213 static int admin_exec(struct ast_channel *chan, void *data) {
2214         char *params;
2215         struct ast_conference *cnf;
2216         struct ast_conf_user *user = NULL;
2217         struct localuser *u;
2218         AST_DECLARE_APP_ARGS(args,
2219                 AST_APP_ARG(confno);
2220                 AST_APP_ARG(command);
2221                 AST_APP_ARG(user);
2222         );
2223         
2224         LOCAL_USER_ADD(u);
2225
2226         AST_LIST_LOCK(&confs);
2227         /* The param has the conference number the user and the command to execute */
2228         if (!ast_strlen_zero(data)) {           
2229                 params = ast_strdupa((char *) data);
2230
2231                 AST_STANDARD_APP_ARGS(args, params);
2232
2233                 if (!args.command) {
2234                         ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
2235                         AST_LIST_UNLOCK(&confs);
2236                         LOCAL_USER_REMOVE(u);
2237                         return -1;
2238                 }
2239                 AST_LIST_TRAVERSE(&confs, cnf, list) {
2240                         if (!strcmp(cnf->confno, args.confno))
2241                                 break;
2242                 }
2243                 
2244                 if (args.user)
2245                         user = find_user(cnf, args.user);
2246                 
2247                 if (cnf) {
2248                         switch((int) (*args.command)) {
2249                         case 76: /* L: Lock */ 
2250                                 cnf->locked = 1;
2251                                 break;
2252                         case 108: /* l: Unlock */ 
2253                                 cnf->locked = 0;
2254                                 break;
2255                         case 75: /* K: kick all users */
2256                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2257                                         user->adminflags |= ADMINFLAG_KICKME;
2258                                 break;
2259                         case 101: /* e: Eject last user*/
2260                                 user = AST_LIST_LAST(&cnf->userlist);
2261                                 if (!(user->userflags & CONFFLAG_ADMIN))
2262                                         user->adminflags |= ADMINFLAG_KICKME;
2263                                 else
2264                                         ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
2265                                 break;
2266                         case 77: /* M: Mute */ 
2267                                 if (user) {
2268                                         user->adminflags |= ADMINFLAG_MUTED;
2269                                         manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2270                                                 "Status: on"
2271                                                 "Meetme: %s\r\n"
2272                                                 "Usernum: %d\r\n",
2273                                                 cnf->confno, user->user_no);
2274                                 } else {
2275                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2276                                 }
2277                                 break;
2278                         case 78: /* N: Mute all users */
2279                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
2280                                         if (!(user->userflags & CONFFLAG_ADMIN))
2281                                                 user->adminflags |= ADMINFLAG_MUTED;
2282                                 }
2283                                 break;                                  
2284                         case 109: /* m: Unmute */ 
2285                                 if (user && (user->adminflags & ADMINFLAG_MUTED)) {
2286                                         user->adminflags ^= ADMINFLAG_MUTED;
2287                                         manager_event(EVENT_FLAG_CALL, "MeetmeMute",
2288                                                 "Status: off"
2289                                                 "Meetme: %s\r\n"
2290                                                 "Usernum: %d\r\n",
2291                                                 cnf->confno, user->user_no);
2292                                 } else {
2293                                         ast_log(LOG_NOTICE, "Specified User not found or he muted himself!\n");
2294                                 }
2295                                 break;
2296                         case 110: /* n: Unmute all users */
2297                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2298                                         user->adminflags &= ~ADMINFLAG_MUTED;
2299                                 break;
2300                         case 107: /* k: Kick user */ 
2301                                 if (user)
2302                                         user->adminflags |= ADMINFLAG_KICKME;
2303                                 else
2304                                         ast_log(LOG_NOTICE, "Specified User not found!");
2305                                 break;
2306                         }
2307                 } else {
2308                         ast_log(LOG_NOTICE, "Conference Number not found\n");
2309                 }
2310         }
2311         AST_LIST_UNLOCK(&confs);
2312
2313         LOCAL_USER_REMOVE(u);
2314         
2315         return 0;
2316 }
2317
2318 static void *recordthread(void *args)
2319 {
2320         struct ast_conference *cnf = args;
2321         struct ast_frame *f=NULL;
2322         int flags;
2323         struct ast_filestream *s=NULL;
2324         int res=0;
2325         int x;
2326         const char *oldrecordingfilename = NULL;
2327
2328         if (!cnf || !cnf->lchan) {
2329                 pthread_exit(0);
2330         }
2331
2332         ast_stopstream(cnf->lchan);
2333         flags = O_CREAT|O_TRUNC|O_WRONLY;
2334
2335
2336         cnf->recording = MEETME_RECORD_ACTIVE;
2337         while (ast_waitfor(cnf->lchan, -1) > -1) {
2338                 if (cnf->recording == MEETME_RECORD_TERMINATE) {
2339                         AST_LIST_LOCK(&confs);
2340                         AST_LIST_UNLOCK(&confs);
2341                         break;
2342                 }
2343                 if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
2344                         s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
2345                         oldrecordingfilename = cnf->recordingfilename;
2346                 }
2347                 
2348                 f = ast_read(cnf->lchan);
2349                 if (!f) {
2350                         res = -1;
2351                         break;
2352                 }
2353                 if (f->frametype == AST_FRAME_VOICE) {
2354                         ast_mutex_lock(&cnf->listenlock);
2355                         for (x=0;x<AST_FRAME_BITS;x++) {
2356                                 /* Free any translations that have occured */
2357                                 if (cnf->transframe[x]) {
2358                                         ast_frfree(cnf->transframe[x]);
2359                                         cnf->transframe[x] = NULL;
2360                                 }
2361                         }
2362                         if (cnf->origframe)
2363                                 ast_frfree(cnf->origframe);
2364                         cnf->origframe = f;
2365                         ast_mutex_unlock(&cnf->listenlock);
2366                         if (s)
2367                                 res = ast_writestream(s, f);
2368                         if (res) {
2369                                 ast_frfree(f);
2370                                 break;
2371                         }
2372                 }
2373                 ast_frfree(f);
2374         }
2375         cnf->recording = MEETME_RECORD_OFF;
2376         if (s)
2377                 ast_closestream(s);
2378         
2379         pthread_exit(0);
2380 }
2381
2382 static void load_config(void)
2383 {
2384         struct ast_config *cfg;
2385         char *val;
2386
2387         audio_buffers = DEFAULT_AUDIO_BUFFERS;
2388
2389         if (!(cfg = ast_config_load(CONFIG_FILE_NAME)))
2390                 return;
2391
2392         if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
2393                 if ((sscanf(val, "%d", &audio_buffers) != 1)) {
2394                         ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
2395                         audio_buffers = DEFAULT_AUDIO_BUFFERS;
2396                 } else if ((audio_buffers < ZT_DEFAULT_NUM_BUFS) || (audio_buffers > ZT_MAX_NUM_BUFS)) {
2397                         ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
2398                                 ZT_DEFAULT_NUM_BUFS, ZT_MAX_NUM_BUFS);
2399                         audio_buffers = DEFAULT_AUDIO_BUFFERS;
2400                 }
2401                 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
2402                         ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
2403         }
2404
2405         ast_config_destroy(cfg);
2406 }
2407
2408 static int unload_module(void *mod)
2409 {
2410         int res;
2411         
2412         res = ast_cli_unregister(&cli_conf);
2413         res |= ast_unregister_application(app3);
2414         res |= ast_unregister_application(app2);
2415         res |= ast_unregister_application(app);
2416
2417         STANDARD_HANGUP_LOCALUSERS;
2418
2419         return res;
2420 }
2421
2422 static int load_module(void *mod)
2423 {
2424         int res;
2425
2426         load_config();
2427
2428         res = ast_cli_register(&cli_conf);
2429         res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
2430         res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
2431         res |= ast_register_application(app, conf_exec, synopsis, descrip);
2432
2433         return res;
2434 }
2435
2436 static int reload(void *mod)
2437 {
2438         load_config();
2439
2440         return 0;
2441 }
2442
2443 static const char *description(void)
2444 {
2445         return "MeetMe conference bridge";
2446 }
2447
2448 static const char *key(void)
2449 {
2450         return ASTERISK_GPL_KEY;
2451 }
2452
2453 STD_MOD(MOD_1, reload, NULL, NULL);
2454