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