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