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