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