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