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