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