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