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