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