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