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