Don't double check existence of a variable (issue #7300 reported by foamdino)
[asterisk/asterisk.git] / apps / app_meetme.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Meet me conference bridge
22  *
23  * \author Mark Spencer <markster@digium.com>
24  * 
25  * \ingroup applications
26  */
27
28 /*** MODULEINFO
29         <depend>zaptel</depend>
30  ***/
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <errno.h>
41 #include <sys/ioctl.h>
42 #ifdef __linux__
43 #include <linux/zaptel.h>
44 #else
45 #include <zaptel.h>
46 #endif /* __linux__ */
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 self-muted */
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\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 one user\n"
254 "      'M' -- Mute one user\n"
255 "      'n' -- Unmute all users in the conference\n"
256 "      'N' -- Mute all non-admin users in the conference\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->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
706                                         user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(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->userflags  & CONFFLAG_MONITOR ? "1" : "",
716                                         user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)  ? "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 = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
968         user->talking = -1;
969         conf->users++;
970         /* Update table */
971         snprintf(members, sizeof(members), "%d", conf->users);
972         ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL);
973
974         ast_mutex_unlock(&conf->playlock);
975
976         if (confflags & CONFFLAG_EXIT_CONTEXT) {
977                 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) 
978                         ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
979                 else if (!ast_strlen_zero(chan->macrocontext)) 
980                         ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
981                 else
982                         ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
983         }
984
985         if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
986                 snprintf(user->namerecloc, sizeof(user->namerecloc),
987                          "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
988                          conf->confno, user->user_no);
989                 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
990                         res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
991                 else
992                         res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
993                 if (res == -1)
994                         goto outrun;
995         }
996
997         if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
998                 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
999                         if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1000                                 ast_waitstream(chan, "");
1001                 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1002                         if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1003                                 ast_waitstream(chan, "");
1004         }
1005
1006         if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1007                 int keepplaying = 1;
1008
1009                 if (conf->users == 2) { 
1010                         if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1011                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
1012                                 if (res > 0)
1013                                         keepplaying=0;
1014                                 else if (res == -1)
1015                                         goto outrun;
1016                         }
1017                 } else { 
1018                         if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1019                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
1020                                 if (res > 0)
1021                                         keepplaying=0;
1022                                 else if (res == -1)
1023                                         goto outrun;
1024                         }
1025                         if (keepplaying) {
1026                                 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1027                                 if (res > 0)
1028                                         keepplaying=0;
1029                                 else if (res == -1)
1030                                         goto outrun;
1031                         }
1032                         if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1033                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
1034                                 if (res > 0)
1035                                         keepplaying=0;
1036                                 else if (res == -1) 
1037                                         goto outrun;
1038                         }
1039                 }
1040         }
1041
1042         ast_indicate(chan, -1);
1043
1044         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1045                 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1046                 goto outrun;
1047         }
1048
1049         if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1050                 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1051                 goto outrun;
1052         }
1053
1054         retryzap = strcasecmp(chan->tech->type, "Zap");
1055         user->zapchannel = !retryzap;
1056
1057  zapretry:
1058         origfd = chan->fds[0];
1059         if (retryzap) {
1060                 fd = open("/dev/zap/pseudo", O_RDWR);
1061                 if (fd < 0) {
1062                         ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1063                         goto outrun;
1064                 }
1065                 using_pseudo = 1;
1066                 /* Make non-blocking */
1067                 flags = fcntl(fd, F_GETFL);
1068                 if (flags < 0) {
1069                         ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1070                         close(fd);
1071                         goto outrun;
1072                 }
1073                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1074                         ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1075                         close(fd);
1076                         goto outrun;
1077                 }
1078                 /* Setup buffering information */
1079                 memset(&bi, 0, sizeof(bi));
1080                 bi.bufsize = CONF_SIZE/2;
1081                 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1082                 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1083                 bi.numbufs = audio_buffers;
1084                 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1085                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1086                         close(fd);
1087                         goto outrun;
1088                 }
1089                 x = 1;
1090                 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1091                         ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1092                         close(fd);
1093                         goto outrun;
1094                 }
1095                 nfds = 1;
1096         } else {
1097                 /* XXX Make sure we're not running on a pseudo channel XXX */
1098                 fd = chan->fds[0];
1099                 nfds = 0;
1100         }
1101         memset(&ztc, 0, sizeof(ztc));
1102         memset(&ztc_empty, 0, sizeof(ztc_empty));
1103         /* Check to see if we're in a conference... */
1104         ztc.chan = 0;   
1105         if (ioctl(fd, ZT_GETCONF, &ztc)) {
1106                 ast_log(LOG_WARNING, "Error getting conference\n");
1107                 close(fd);
1108                 goto outrun;
1109         }
1110         if (ztc.confmode) {
1111                 /* Whoa, already in a conference...  Retry... */
1112                 if (!retryzap) {
1113                         ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
1114                         retryzap = 1;
1115                         goto zapretry;
1116                 }
1117         }
1118         memset(&ztc, 0, sizeof(ztc));
1119         /* Add us to the conference */
1120         ztc.chan = 0;   
1121         ztc.confno = conf->zapconf;
1122
1123         ast_mutex_lock(&conf->playlock);
1124
1125         if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1126                 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1127                         if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1128                                 ast_waitstream(conf->chan, "");
1129                         if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1130                                 ast_waitstream(conf->chan, "");
1131                 }
1132         }
1133
1134         if (confflags & CONFFLAG_MONITOR)
1135                 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1136         else if (confflags & CONFFLAG_TALKER)
1137                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1138         else 
1139                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1140
1141         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1142                 ast_log(LOG_WARNING, "Error setting conference\n");
1143                 close(fd);
1144                 ast_mutex_unlock(&conf->playlock);
1145                 goto outrun;
1146         }
1147         ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1148
1149         if (!sent_event) {
1150                 manager_event(EVENT_FLAG_CALL, "MeetmeJoin", 
1151                               "Channel: %s\r\n"
1152                               "Uniqueid: %s\r\n"
1153                               "Meetme: %s\r\n"
1154                               "Usernum: %d\r\n",
1155                               chan->name, chan->uniqueid, conf->confno, user->user_no);
1156                 sent_event = 1;
1157         }
1158
1159         if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1160                 firstpass = 1;
1161                 if (!(confflags & CONFFLAG_QUIET))
1162                         if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1163                                 conf_play(chan, conf, ENTER);
1164         }
1165
1166         ast_mutex_unlock(&conf->playlock);
1167
1168         conf_flush(fd, chan);
1169
1170         if (confflags & CONFFLAG_AGI) {
1171                 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1172                    or use default filename of conf-background.agi */
1173
1174                 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1175                 if (!agifile)
1176                         agifile = agifiledefault;
1177
1178                 if (user->zapchannel) {
1179                         /*  Set CONFMUTE mode on Zap channel to mute DTMF tones */
1180                         x = 1;
1181                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1182                 }
1183                 /* Find a pointer to the agi app and execute the script */
1184                 app = pbx_findapp("agi");
1185                 if (app) {
1186                         char *s = ast_strdupa(agifile);
1187                         ret = pbx_exec(chan, app, s);
1188                 } else {
1189                         ast_log(LOG_WARNING, "Could not find application (agi)\n");
1190                         ret = -2;
1191                 }
1192                 if (user->zapchannel) {
1193                         /*  Remove CONFMUTE mode on Zap channel */
1194                         x = 0;
1195                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1196                 }
1197         } else {
1198                 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1199                         /*  Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1200                         x = 1;
1201                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1202                 }       
1203                 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER) && !(dsp = ast_dsp_new())) {
1204                         ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1205                         res = -1;
1206                 }
1207                 for(;;) {
1208                         int menu_was_active = 0;
1209
1210                         outfd = -1;
1211                         ms = -1;
1212                         
1213                         /* if we have just exited from the menu, and the user had a channel-driver
1214                            volume adjustment, restore it
1215                         */
1216                         if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
1217                                 set_talk_volume(user, user->listen.desired);
1218
1219                         menu_was_active = menu_active;
1220
1221                         currentmarked = conf->markedusers;
1222                         if (!(confflags & CONFFLAG_QUIET) &&
1223                             (confflags & CONFFLAG_MARKEDUSER) &&
1224                             (confflags & CONFFLAG_WAITMARKED) &&
1225                             lastmarked == 0) {
1226                                 if (currentmarked == 1 && conf->users > 1) {
1227                                         ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1228                                         if (conf->users - 1 == 1) {
1229                                                 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
1230                                                         ast_waitstream(chan, "");
1231                                         } else {
1232                                                 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
1233                                                         ast_waitstream(chan, "");
1234                                         }
1235                                 }
1236                                 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
1237                                         if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1238                                                 ast_waitstream(chan, "");
1239                         }
1240
1241                         c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
1242                         
1243                         
1244                         /* Update the struct with the actual confflags */
1245                         user->userflags = confflags;
1246                         
1247                         if (confflags & CONFFLAG_WAITMARKED) {
1248                                 if(currentmarked == 0) {
1249                                         if (lastmarked != 0) {
1250                                                 if (!(confflags & CONFFLAG_QUIET))
1251                                                         if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
1252                                                                 ast_waitstream(chan, "");
1253                                                 if(confflags & CONFFLAG_MARKEDEXIT)
1254                                                         break;
1255                                                 else {
1256                                                         ztc.confmode = ZT_CONF_CONF;
1257                                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1258                                                                 ast_log(LOG_WARNING, "Error setting conference\n");
1259                                                                 close(fd);
1260                                                                 goto outrun;
1261                                                         }
1262                                                 }
1263                                         }
1264                                         if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
1265                                                 ast_moh_start(chan, NULL);
1266                                                 musiconhold = 1;
1267                                         } else {
1268                                                 ztc.confmode = ZT_CONF_CONF;
1269                                                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1270                                                         ast_log(LOG_WARNING, "Error setting conference\n");
1271                                                         close(fd);
1272                                                         goto outrun;
1273                                                 }
1274                                         }
1275                                 } else if(currentmarked >= 1 && lastmarked == 0) {
1276                                         if (confflags & CONFFLAG_MONITOR)
1277                                                 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1278                                         else if (confflags & CONFFLAG_TALKER)
1279                                                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1280                                         else
1281                                                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1282                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1283                                                 ast_log(LOG_WARNING, "Error setting conference\n");
1284                                                 close(fd);
1285                                                 goto outrun;
1286                                         }
1287                                         if (musiconhold && (confflags & CONFFLAG_MOH)) {
1288                                                 ast_moh_stop(chan);
1289                                                 musiconhold = 0;
1290                                         }
1291                                         if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
1292                                                 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
1293                                                         ast_waitstream(chan, "");
1294                                                 conf_play(chan, conf, ENTER);
1295                                         }
1296                                 }
1297                         }
1298
1299                         /* trying to add moh for single person conf */
1300                         if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
1301                                 if (conf->users == 1) {
1302                                         if (musiconhold == 0) {
1303                                                 ast_moh_start(chan, NULL);
1304                                                 musiconhold = 1;
1305                                         } 
1306                                 } else {
1307                                         if (musiconhold) {
1308                                                 ast_moh_stop(chan);
1309                                                 musiconhold = 0;
1310                                         }
1311                                 }
1312                         }
1313                         
1314                         /* Leave if the last marked user left */
1315                         if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
1316                                 ret = -1;
1317                                 break;
1318                         }
1319         
1320                         /* Check if my modes have changed */
1321
1322                         /* If I should be muted but am still talker, mute me */
1323                         if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
1324                                 ztc.confmode ^= ZT_CONF_TALKER;
1325                                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1326                                         ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1327                                         ret = -1;
1328                                         break;
1329                                 }
1330
1331                                 manager_event(EVENT_FLAG_CALL, "MeetmeMute", 
1332                                                 "Channel: %s\r\n"
1333                                                 "Uniqueid: %s\r\n"
1334                                                 "Meetme: %s\r\n"
1335                                                 "Usernum: %i\r\n"
1336                                                 "Status: on\r\n",
1337                                                 chan->name, chan->uniqueid, conf->confno, user->user_no);
1338                         }
1339
1340                         /* If I should be un-muted but am not talker, un-mute me */
1341                         if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1342                                 ztc.confmode |= ZT_CONF_TALKER;
1343                                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1344                                         ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1345                                         ret = -1;
1346                                         break;
1347                                 }
1348
1349                                 manager_event(EVENT_FLAG_CALL, "MeetmeMute", 
1350                                                 "Channel: %s\r\n"
1351                                                 "Uniqueid: %s\r\n"
1352                                                 "Meetme: %s\r\n"
1353                                                 "Usernum: %i\r\n"
1354                                                 "Status: off\r\n",
1355                                                 chan->name, chan->uniqueid, conf->confno, user->user_no);
1356                         }
1357
1358                         /* If I have been kicked, exit the conference */
1359                         if (user->adminflags & ADMINFLAG_KICKME) {
1360                                 //You have been kicked.
1361                                 if (!ast_streamfile(chan, "conf-kicked", chan->language))
1362                                         ast_waitstream(chan, "");
1363                                 ret = 0;
1364                                 break;
1365                         }
1366
1367                         if (c) {
1368                                 if (c->fds[0] != origfd) {
1369                                         if (using_pseudo) {
1370                                                 /* Kill old pseudo */
1371                                                 close(fd);
1372                                                 using_pseudo = 0;
1373                                         }
1374                                         ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
1375                                         retryzap = strcasecmp(c->tech->type, "Zap");
1376                                         user->zapchannel = !retryzap;
1377                                         goto zapretry;
1378                                 }
1379                                 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
1380                                         f = ast_read_noaudio(c);
1381                                 else
1382                                         f = ast_read(c);
1383                                 if (!f)
1384                                         break;
1385                                 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1386                                         if (user->talk.actual)
1387                                                 ast_frame_adjust_volume(f, user->talk.actual);
1388
1389                                         if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER)) {
1390                                                 int totalsilence;
1391
1392                                                 if (user->talking == -1)
1393                                                         user->talking = 0;
1394
1395                                                 res = ast_dsp_silence(dsp, f, &totalsilence);
1396                                                 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1397                                                         user->talking = 1;
1398                                                         if (confflags & CONFFLAG_MONITORTALKER)
1399                                                                 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1400                                                                       "Channel: %s\r\n"
1401                                                                       "Uniqueid: %s\r\n"
1402                                                                       "Meetme: %s\r\n"
1403                                                                       "Usernum: %d\r\n"
1404                                                                       "Status: on\r\n",
1405                                                                       chan->name, chan->uniqueid, conf->confno, user->user_no);
1406                                                 }
1407                                                 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1408                                                         user->talking = 0;
1409                                                         if (confflags & CONFFLAG_MONITORTALKER)
1410                                                                 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1411                                                                       "Channel: %s\r\n"
1412                                                                       "Uniqueid: %s\r\n"
1413                                                                       "Meetme: %s\r\n"
1414                                                                       "Usernum: %d\r\n"
1415                                                                       "Status: off\r\n",
1416                                                                       chan->name, chan->uniqueid, conf->confno, user->user_no);
1417                                                 }
1418                                         }
1419                                         if (using_pseudo) {
1420                                                 /* Absolutely do _not_ use careful_write here...
1421                                                    it is important that we read data from the channel
1422                                                    as fast as it arrives, and feed it into the conference.
1423                                                    The buffering in the pseudo channel will take care of any
1424                                                    timing differences, unless they are so drastic as to lose
1425                                                    audio frames (in which case carefully writing would only
1426                                                    have delayed the audio even further).
1427                                                 */
1428                                                 /* As it turns out, we do want to use careful write.  We just
1429                                                    don't want to block, but we do want to at least *try*
1430                                                    to write out all the samples.
1431                                                  */
1432                                                 if (user->talking || !(confflags & CONFFLAG_OPTIMIZETALKER))
1433                                                         careful_write(fd, f->data, f->datalen, 0);
1434                                         }
1435                                 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
1436                                         char tmp[2];
1437
1438                                         tmp[0] = f->subclass;
1439                                         tmp[1] = '\0';
1440                                         if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
1441                                                 ast_log(LOG_DEBUG, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
1442                                                 ret = 0;
1443                                                 break;
1444                                         } else if (option_debug > 1)
1445                                                 ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
1446                                 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
1447                                         ret = 0;
1448                                         break;
1449                                 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
1450                                         if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
1451                                                 ast_log(LOG_WARNING, "Error setting conference\n");
1452                                                 close(fd);
1453                                                 goto outrun;
1454                                         }
1455
1456                                         /* if we are entering the menu, and the user has a channel-driver
1457                                            volume adjustment, clear it
1458                                         */
1459                                         if (!menu_active && user->talk.desired && !user->talk.actual)
1460                                                 set_talk_volume(user, 0);
1461
1462                                         if (musiconhold) {
1463                                                 ast_moh_stop(chan);
1464                                         }
1465                                         if ((confflags & CONFFLAG_ADMIN)) {
1466                                                 /* Admin menu */
1467                                                 if (!menu_active) {
1468                                                         menu_active = 1;
1469                                                         /* Record this sound! */
1470                                                         if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
1471                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1472                                                                 ast_stopstream(chan);
1473                                                         } else 
1474                                                                 dtmf = 0;
1475                                                 } else 
1476                                                         dtmf = f->subclass;
1477                                                 if (dtmf) {
1478                                                         switch(dtmf) {
1479                                                         case '1': /* Un/Mute */
1480                                                                 menu_active = 0;
1481
1482                                                                 /* for admin, change both admin and use flags */
1483                                                                 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
1484                                                                         user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
1485                                                                 else
1486                                                                         user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
1487
1488                                                                 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
1489                                                                         if (!ast_streamfile(chan, "conf-muted", chan->language))
1490                                                                                 ast_waitstream(chan, "");
1491                                                                 } else {
1492                                                                         if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1493                                                                                 ast_waitstream(chan, "");
1494                                                                 }
1495                                                                 break;
1496                                                         case '2': /* Un/Lock the Conference */
1497                                                                 menu_active = 0;
1498                                                                 if (conf->locked) {
1499                                                                         conf->locked = 0;
1500                                                                         if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1501                                                                                 ast_waitstream(chan, "");
1502                                                                 } else {
1503                                                                         conf->locked = 1;
1504                                                                         if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1505                                                                                 ast_waitstream(chan, "");
1506                                                                 }
1507                                                                 break;
1508                                                         case '3': /* Eject last user */
1509                                                                 menu_active = 0;
1510                                                                 usr = AST_LIST_LAST(&conf->userlist);
1511                                                                 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1512                                                                         if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1513                                                                                 ast_waitstream(chan, "");
1514                                                                 } else 
1515                                                                         usr->adminflags |= ADMINFLAG_KICKME;
1516                                                                 ast_stopstream(chan);
1517                                                                 break;  
1518                                                         case '4':
1519                                                                 tweak_listen_volume(user, VOL_DOWN);
1520                                                                 break;
1521                                                         case '6':
1522                                                                 tweak_listen_volume(user, VOL_UP);
1523                                                                 break;
1524                                                         case '7':
1525                                                                 tweak_talk_volume(user, VOL_DOWN);
1526                                                                 break;
1527                                                         case '8':
1528                                                                 menu_active = 0;
1529                                                                 break;
1530                                                         case '9':
1531                                                                 tweak_talk_volume(user, VOL_UP);
1532                                                                 break;
1533                                                         default:
1534                                                                 menu_active = 0;
1535                                                                 /* Play an error message! */
1536                                                                 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1537                                                                         ast_waitstream(chan, "");
1538                                                                 break;
1539                                                         }
1540                                                 }
1541                                         } else {
1542                                                 /* User menu */
1543                                                 if (!menu_active) {
1544                                                         menu_active = 1;
1545                                                         if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
1546                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1547                                                                 ast_stopstream(chan);
1548                                                         } else
1549                                                                 dtmf = 0;
1550                                                 } else 
1551                                                         dtmf = f->subclass;
1552                                                 if (dtmf) {
1553                                                         switch(dtmf) {
1554                                                         case '1': /* Un/Mute */
1555                                                                 menu_active = 0;
1556
1557                                                                 /* user can only toggle the self-muted state */
1558                                                                 user->adminflags ^= ADMINFLAG_SELFMUTED;
1559
1560                                                                 /* they can't override the admin mute state */
1561                                                                 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
1562                                                                         if (!ast_streamfile(chan, "conf-muted", chan->language))
1563                                                                                 ast_waitstream(chan, "");
1564                                                                 } else {
1565                                                                         if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1566                                                                                 ast_waitstream(chan, "");
1567                                                                 }
1568                                                                 break;
1569                                                         case '4':
1570                                                                 tweak_listen_volume(user, VOL_DOWN);
1571                                                                 break;
1572                                                         case '6':
1573                                                                 tweak_listen_volume(user, VOL_UP);
1574                                                                 break;
1575                                                         case '7':
1576                                                                 tweak_talk_volume(user, VOL_DOWN);
1577                                                                 break;
1578                                                         case '8':
1579                                                                 menu_active = 0;
1580                                                                 break;
1581                                                         case '9':
1582                                                                 tweak_talk_volume(user, VOL_UP);
1583                                                                 break;
1584                                                         default:
1585                                                                 menu_active = 0;
1586                                                                 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1587                                                                         ast_waitstream(chan, "");
1588                                                                 break;
1589                                                         }
1590                                                 }
1591                                         }
1592                                         if (musiconhold)
1593                                                 ast_moh_start(chan, NULL);
1594
1595                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1596                                                 ast_log(LOG_WARNING, "Error setting conference\n");
1597                                                 close(fd);
1598                                                 AST_LIST_UNLOCK(&confs);
1599                                                 goto outrun;
1600                                         }
1601
1602                                         conf_flush(fd, chan);
1603                                 } else if (option_debug) {
1604                                         ast_log(LOG_DEBUG,
1605                                                 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
1606                                                 chan->name, f->frametype, f->subclass);
1607                                 }
1608                                 ast_frfree(f);
1609                         } else if (outfd > -1) {
1610                                 res = read(outfd, buf, CONF_SIZE);
1611                                 if (res > 0) {
1612                                         memset(&fr, 0, sizeof(fr));
1613                                         fr.frametype = AST_FRAME_VOICE;
1614                                         fr.subclass = AST_FORMAT_SLINEAR;
1615                                         fr.datalen = res;
1616                                         fr.samples = res/2;
1617                                         fr.data = buf;
1618                                         fr.offset = AST_FRIENDLY_OFFSET;
1619                                         if (!user->listen.actual && 
1620                                                 ((confflags & CONFFLAG_MONITOR) || 
1621                                                  (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
1622                                                  (!user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
1623                                                  )) {
1624                                                 int index;
1625                                                 for (index=0;index<AST_FRAME_BITS;index++)
1626                                                         if (chan->rawwriteformat & (1 << index))
1627                                                                 break;
1628                                                 if (index >= AST_FRAME_BITS)
1629                                                         goto bailoutandtrynormal;
1630                                                 ast_mutex_lock(&conf->listenlock);
1631                                                 if (!conf->transframe[index]) {
1632                                                         if (conf->origframe) {
1633                                                                 if (!conf->transpath[index])
1634                                                                         conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
1635                                                                 if (conf->transpath[index]) {
1636                                                                         conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
1637                                                                         if (!conf->transframe[index])
1638                                                                                 conf->transframe[index] = &ast_null_frame;
1639                                                                 }
1640                                                         }
1641                                                 }
1642                                                 if (conf->transframe[index]) {
1643                                                         if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
1644                                                                 if (ast_write(chan, conf->transframe[index]))
1645                                                                         ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1646                                                         }
1647                                                 } else {
1648                                                         ast_mutex_unlock(&conf->listenlock);
1649                                                         goto bailoutandtrynormal;
1650                                                 }
1651                                                 ast_mutex_unlock(&conf->listenlock);
1652                                         } else {
1653 bailoutandtrynormal:                                    
1654                                                 if (user->listen.actual)
1655                                                         ast_frame_adjust_volume(&fr, user->listen.actual);
1656                                                 if (ast_write(chan, &fr) < 0) {
1657                                                         ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1658                                                 }
1659                                         }
1660                                 } else 
1661                                         ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1662                         }
1663                         lastmarked = currentmarked;
1664                 }
1665         }
1666
1667         if (musiconhold)
1668                 ast_moh_stop(chan);
1669         
1670         if (using_pseudo)
1671                 close(fd);
1672         else {
1673                 /* Take out of conference */
1674                 ztc.chan = 0;   
1675                 ztc.confno = 0;
1676                 ztc.confmode = 0;
1677                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1678                         ast_log(LOG_WARNING, "Error setting conference\n");
1679                 }
1680         }
1681
1682         reset_volumes(user);
1683
1684         AST_LIST_LOCK(&confs);
1685         if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1686                 conf_play(chan, conf, LEAVE);
1687
1688         if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1689                 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
1690                         if ((conf->chan) && (conf->users > 1)) {
1691                                 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1692                                         ast_waitstream(conf->chan, "");
1693                                 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1694                                         ast_waitstream(conf->chan, "");
1695                         }
1696                         ast_filedelete(user->namerecloc, NULL);
1697                 }
1698         }
1699         AST_LIST_UNLOCK(&confs);
1700
1701  outrun:
1702         AST_LIST_LOCK(&confs);
1703
1704         if (dsp)
1705                 ast_dsp_free(dsp);
1706         
1707         if (user->user_no) { /* Only cleanup users who really joined! */
1708                 now = time(NULL);
1709                 hr = (now - user->jointime) / 3600;
1710                 min = ((now - user->jointime) % 3600) / 60;
1711                 sec = (now - user->jointime) % 60;
1712
1713                 if (sent_event) {
1714                         manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
1715                                       "Channel: %s\r\n"
1716                                       "Uniqueid: %s\r\n"
1717                                       "Meetme: %s\r\n"
1718                                       "Usernum: %d\r\n"
1719                                       "CallerIDnum: %s\r\n"
1720                                       "CallerIDname: %s\r\n"
1721                                       "Duration: %ld\r\n",
1722                                       chan->name, chan->uniqueid, conf->confno, 
1723                                       user->user_no,
1724                                       S_OR(user->chan->cid.cid_num, "<unknown>"),
1725                                       S_OR(user->chan->cid.cid_name, "<unknown>"),
1726                                       (now - user->jointime));
1727                 }
1728
1729                 conf->users--;
1730                 conf->refcount--;
1731                 /* Update table */
1732                 snprintf(members, sizeof(members), "%d", conf->users);
1733                 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1734                 if (confflags & CONFFLAG_MARKEDUSER) 
1735                         conf->markedusers--;
1736                 /* Remove ourselves from the list */
1737                 AST_LIST_REMOVE(&conf->userlist, user, list);
1738                 if (AST_LIST_EMPTY(&conf->userlist)) {
1739                         /* close this one when no more users and no references*/
1740                         if (!conf->refcount)
1741                                 conf_free(conf);
1742                 }
1743                 /* Return the number of seconds the user was in the conf */
1744                 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
1745                 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1746         }
1747         free(user);
1748         AST_LIST_UNLOCK(&confs);
1749
1750         return ret;
1751 }
1752
1753 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
1754                                                  char *dynamic_pin, int refcount, struct ast_flags *confflags)
1755 {
1756         struct ast_variable *var;
1757         struct ast_conference *cnf;
1758
1759         /* Check first in the conference list */
1760         AST_LIST_LOCK(&confs);
1761         AST_LIST_TRAVERSE(&confs, cnf, list) {
1762                 if (!strcmp(confno, cnf->confno)) 
1763                         break;
1764         }
1765         if (cnf){
1766                 cnf->refcount += refcount;
1767         }
1768         AST_LIST_UNLOCK(&confs);
1769
1770         if (!cnf) {
1771                 char *pin = NULL, *pinadmin = NULL; /* For temp use */
1772
1773                 cnf = ast_calloc(1, sizeof(struct ast_conference));
1774                 if (!cnf) {
1775                         ast_log(LOG_ERROR, "Out of memory\n");
1776                         return NULL;
1777                 }
1778
1779                 var = ast_load_realtime("meetme", "confno", confno, NULL);
1780                 while (var) {
1781                         if (!strcasecmp(var->name, "confno")) {
1782                                 ast_copy_string(cnf->confno, var->value, sizeof(cnf->confno));
1783                         } else if (!strcasecmp(var->name, "pin")) {
1784                                 pin = ast_strdupa(var->value);
1785                         } else if (!strcasecmp(var->name, "adminpin")) {
1786                                 pinadmin = ast_strdupa(var->value);
1787                         }
1788                         var = var->next;
1789                 }
1790                 ast_variables_destroy(var);
1791
1792                 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount);
1793         }
1794
1795         if (cnf) {
1796                 if (confflags && !cnf->chan &&
1797                     !ast_test_flag(confflags, CONFFLAG_QUIET) &&
1798                     ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
1799                         ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
1800                         ast_clear_flag(confflags, CONFFLAG_INTROUSER);
1801                 }
1802                 
1803                 if (confflags && !cnf->chan &&
1804                     ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
1805                         ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
1806                         ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
1807                 }
1808         }
1809
1810         return cnf;
1811 }
1812
1813
1814 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
1815                                         char *dynamic_pin, int refcount, struct ast_flags *confflags)
1816 {
1817         struct ast_config *cfg;
1818         struct ast_variable *var;
1819         struct ast_conference *cnf;
1820         char *parse;
1821         AST_DECLARE_APP_ARGS(args,
1822                 AST_APP_ARG(confno);
1823                 AST_APP_ARG(pin);
1824                 AST_APP_ARG(pinadmin);
1825         );
1826
1827         /* Check first in the conference list */
1828         AST_LIST_LOCK(&confs);
1829         AST_LIST_TRAVERSE(&confs, cnf, list) {
1830                 if (!strcmp(confno, cnf->confno)) 
1831                         break;
1832         }
1833         if (cnf){
1834                 cnf->refcount += refcount;
1835         }
1836         AST_LIST_UNLOCK(&confs);
1837
1838         if (!cnf) {
1839                 if (dynamic) {
1840                         /* No need to parse meetme.conf */
1841                         ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1842                         if (dynamic_pin) {
1843                                 if (dynamic_pin[0] == 'q') {
1844                                         /* Query the user to enter a PIN */
1845                                         if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0) < 0)
1846                                                 return NULL;
1847                                 }
1848                                 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount);
1849                         } else {
1850                                 cnf = build_conf(confno, "", "", make, dynamic, refcount);
1851                         }
1852                 } else {
1853                         /* Check the config */
1854                         cfg = ast_config_load(CONFIG_FILE_NAME);
1855                         if (!cfg) {
1856                                 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
1857                                 return NULL;
1858                         }
1859                         for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
1860                                 if (strcasecmp(var->name, "conf"))
1861                                         continue;
1862                                 
1863                                 if (!(parse = ast_strdupa(var->value)))
1864                                         return NULL;
1865                                 
1866                                 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
1867                                 if (!strcasecmp(args.confno, confno)) {
1868                                         /* Bingo it's a valid conference */
1869                                         cnf = build_conf(args.confno,
1870                                                         S_OR(args.pin, ""),
1871                                                         S_OR(args.pinadmin, ""),
1872                                                         make, dynamic, refcount);
1873                                         break;
1874                                 }
1875                         }
1876                         if (!var) {
1877                                 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1878                         }
1879                         ast_config_destroy(cfg);
1880                 }
1881         } else if (dynamic_pin) {
1882                 /* Correct for the user selecting 'D' instead of 'd' to have
1883                    someone join into a conference that has already been created
1884                    with a pin. */
1885                 if (dynamic_pin[0] == 'q')
1886                         dynamic_pin[0] = '\0';
1887         }
1888
1889         if (cnf) {
1890                 if (confflags && !cnf->chan &&
1891                     !ast_test_flag(confflags, CONFFLAG_QUIET) &&
1892                     ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
1893                         ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
1894                         ast_clear_flag(confflags, CONFFLAG_INTROUSER);
1895                 }
1896                 
1897                 if (confflags && !cnf->chan &&
1898                     ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
1899                         ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
1900                         ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
1901                 }
1902         }
1903
1904         return cnf;
1905 }
1906
1907 /*! \brief The MeetmeCount application */
1908 static int count_exec(struct ast_channel *chan, void *data)
1909 {
1910         struct localuser *u;
1911         int res = 0;
1912         struct ast_conference *conf;
1913         int count;
1914         char *localdata;
1915         char val[80] = "0"; 
1916         AST_DECLARE_APP_ARGS(args,
1917                 AST_APP_ARG(confno);
1918                 AST_APP_ARG(varname);
1919         );
1920
1921         if (ast_strlen_zero(data)) {
1922                 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1923                 return -1;
1924         }
1925
1926         LOCAL_USER_ADD(u);
1927         
1928         if (!(localdata = ast_strdupa(data))) {
1929                 LOCAL_USER_REMOVE(u);
1930                 return -1;
1931         }
1932
1933         AST_STANDARD_APP_ARGS(args, localdata);
1934         
1935         conf = find_conf(chan, args.confno, 0, 0, NULL, 0, NULL);
1936
1937         if (conf)
1938                 count = conf->users;
1939         else
1940                 count = 0;
1941
1942         if (!ast_strlen_zero(args.varname)){
1943                 /* have var so load it and exit */
1944                 snprintf(val, sizeof(val), "%d",count);
1945                 pbx_builtin_setvar_helper(chan, args.varname, val);
1946         } else {
1947                 if (chan->_state != AST_STATE_UP)
1948                         ast_answer(chan);
1949                 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1950         }
1951         LOCAL_USER_REMOVE(u);
1952
1953         return res;
1954 }
1955
1956 /*! \brief The meetme() application */
1957 static int conf_exec(struct ast_channel *chan, void *data)
1958 {
1959         int res=-1;
1960         struct localuser *u;
1961         char confno[AST_MAX_EXTENSION] = "";
1962         int allowretry = 0;
1963         int retrycnt = 0;
1964         struct ast_conference *cnf;
1965         struct ast_flags confflags = {0};
1966         int dynamic = 0;
1967         int empty = 0, empty_no_pin = 0;
1968         int always_prompt = 0;
1969         char *notdata, *info, the_pin[AST_MAX_EXTENSION] = "";
1970         AST_DECLARE_APP_ARGS(args,
1971                 AST_APP_ARG(confno);
1972                 AST_APP_ARG(options);
1973                 AST_APP_ARG(pin);
1974         );
1975
1976         LOCAL_USER_ADD(u);
1977
1978         if (ast_strlen_zero(data)) {
1979                 allowretry = 1;
1980                 notdata = "";
1981         } else {
1982                 notdata = data;
1983         }
1984         
1985         if (chan->_state != AST_STATE_UP)
1986                 ast_answer(chan);
1987
1988         info = ast_strdupa(notdata);
1989
1990         AST_STANDARD_APP_ARGS(args, info);      
1991
1992         if (args.confno) {
1993                 ast_copy_string(confno, args.confno, sizeof(confno));
1994                 if (ast_strlen_zero(confno)) {
1995                         allowretry = 1;
1996                 }
1997         }
1998         
1999         if (args.pin)
2000                 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2001
2002         if (args.options) {
2003                 ast_app_parse_options(meetme_opts, &confflags, NULL, args.options);
2004                 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2005                 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2006                         strcpy(the_pin, "q");
2007
2008                 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2009                 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2010                 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2011         }
2012
2013         do {
2014                 if (retrycnt > 3)
2015                         allowretry = 0;
2016                 if (empty) {
2017                         int i, map[1024] = { 0, };
2018                         struct ast_config *cfg;
2019                         struct ast_variable *var;
2020                         int confno_int;
2021
2022                         AST_LIST_LOCK(&confs);
2023                         AST_LIST_TRAVERSE(&confs, cnf, list) {
2024                                 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
2025                                         /* Disqualify in use conference */
2026                                         if (confno_int >= 0 && confno_int < 1024)
2027                                                 map[confno_int]++;
2028                                 }
2029                         }
2030                         AST_LIST_UNLOCK(&confs);
2031
2032                         /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2033                         if ((empty_no_pin) || (!dynamic)) {
2034                                 cfg = ast_config_load(CONFIG_FILE_NAME);
2035                                 if (cfg) {
2036                                         var = ast_variable_browse(cfg, "rooms");
2037                                         while (var) {
2038                                                 if (!strcasecmp(var->name, "conf")) {
2039                                                         char *stringp = ast_strdupa(var->value);
2040                                                         if (stringp) {
2041                                                                 char *confno_tmp = strsep(&stringp, "|,");
2042                                                                 int found = 0;
2043                                                                 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
2044                                                                         if ((confno_int >= 0) && (confno_int < 1024)) {
2045                                                                                 if (stringp && empty_no_pin) {
2046                                                                                         map[confno_int]++;
2047                                                                                 }
2048                                                                         }
2049                                                                 }
2050                                                                 if (!dynamic) {
2051                                                                         /* For static:  run through the list and see if this conference is empty */
2052                                                                         AST_LIST_LOCK(&confs);
2053                                                                         AST_LIST_TRAVERSE(&confs, cnf, list) {
2054                                                                                 if (!strcmp(confno_tmp, cnf->confno)) {
2055                                                                                         /* The conference exists, therefore it's not empty */
2056                                                                                         found = 1;
2057                                                                                         break;
2058                                                                                 }
2059                                                                         }
2060                                                                         AST_LIST_UNLOCK(&confs);
2061                                                                         if (!found) {
2062                                                                                 /* At this point, we have a confno_tmp (static conference) that is empty */
2063                                                                                 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2064                                                                                         /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
2065                                                                                          * Case 2:  empty_no_pin and pin is blank (but not NULL)
2066                                                                                          * Case 3:  not empty_no_pin
2067                                                                                          */
2068                                                                                         ast_copy_string(confno, confno_tmp, sizeof(confno));
2069                                                                                         break;
2070                                                                                         /* XXX the map is not complete (but we do have a confno) */
2071                                                                                 }
2072                                                                         }
2073                                                                 }
2074                                                         }
2075                                                 }
2076                                                 var = var->next;
2077                                         }
2078                                         ast_config_destroy(cfg);
2079                                 }
2080                         }
2081
2082                         /* Select first conference number not in use */
2083                         if (ast_strlen_zero(confno) && dynamic) {
2084                                 for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
2085                                         if (!map[i]) {
2086                                                 snprintf(confno, sizeof(confno), "%d", i);
2087                                                 break;
2088                                         }
2089                                 }
2090                         }
2091
2092                         /* Not found? */
2093                         if (ast_strlen_zero(confno)) {
2094                                 res = ast_streamfile(chan, "conf-noempty", chan->language);
2095                                 if (!res)
2096                                         ast_waitstream(chan, "");
2097                         } else {
2098                                 if (sscanf(confno, "%d", &confno_int) == 1) {
2099                                         res = ast_streamfile(chan, "conf-enteringno", chan->language);
2100                                         if (!res) {
2101                                                 ast_waitstream(chan, "");
2102                                                 res = ast_say_digits(chan, confno_int, "", chan->language);
2103                                         }
2104                                 } else {
2105                                         ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2106                                 }
2107                         }
2108                 }
2109
2110                 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2111                         /* Prompt user for conference number */
2112                         res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2113                         if (res < 0) {
2114                                 /* Don't try to validate when we catch an error */
2115                                 confno[0] = '\0';
2116                                 allowretry = 0;
2117                                 break;
2118                         }
2119                 }
2120                 if (!ast_strlen_zero(confno)) {
2121                         /* Check the validity of the conference */
2122                         cnf = find_conf(chan, confno, 1, dynamic, the_pin, 1, &confflags);
2123                         if (!cnf)
2124                                 cnf = find_conf_realtime(chan, confno, 1, dynamic, the_pin, 1, &confflags);
2125
2126                         if (!cnf) {
2127                                 res = ast_streamfile(chan, "conf-invalid", chan->language);
2128                                 if (!res)
2129                                         ast_waitstream(chan, "");
2130                                 res = -1;
2131                                 if (allowretry)
2132                                         confno[0] = '\0';
2133                         } else {
2134                                 if ((!ast_strlen_zero(cnf->pin) &&
2135                                      !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2136                                     (!ast_strlen_zero(cnf->pinadmin) &&
2137                                      ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2138                                         char pin[AST_MAX_EXTENSION]="";
2139                                         int j;
2140
2141                                         /* Allow the pin to be retried up to 3 times */
2142                                         for (j = 0; j < 3; j++) {
2143                                                 if (*the_pin && (always_prompt == 0)) {
2144                                                         ast_copy_string(pin, the_pin, sizeof(pin));
2145                                                         res = 0;
2146                                                 } else {
2147                                                         /* Prompt user for pin if pin is required */
2148                                                         res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2149                                                 }
2150                                                 if (res >= 0) {
2151                                                         if (!strcasecmp(pin, cnf->pin) ||
2152                                                             (!ast_strlen_zero(cnf->pinadmin) &&
2153                                                              !strcasecmp(pin, cnf->pinadmin))) {
2154                                                                 /* Pin correct */
2155                                                                 allowretry = 0;
2156                                                                 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) 
2157                                                                         ast_set_flag(&confflags, CONFFLAG_ADMIN);
2158                                                                 /* Run the conference */
2159                                                                 res = conf_run(chan, cnf, confflags.flags);
2160                                                                 break;
2161                                                         } else {
2162                                                                 /* Pin invalid */
2163                                                                 if (!ast_streamfile(chan, "conf-invalidpin", chan->language))
2164                                                                         res = ast_waitstream(chan, AST_DIGIT_ANY);
2165                                                                 else {
2166                                                                         ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
2167                                                                         break;
2168                                                                 }
2169                                                                 if (res < 0) {
2170                                                                         AST_LIST_LOCK(&confs);
2171                                                                         cnf->refcount--;
2172                                                                         if (!cnf->refcount){
2173                                                                                 conf_free(cnf);
2174                                                                         }
2175                                                                         AST_LIST_UNLOCK(&confs);
2176                                                                         break;
2177                                                                 }
2178                                                                 pin[0] = res;
2179                                                                 pin[1] = '\0';
2180                                                                 res = -1;
2181                                                                 if (allowretry)
2182                                                                         confno[0] = '\0';
2183                                                         }
2184                                                 } else {
2185                                                         /* failed when getting the pin */
2186                                                         res = -1;
2187                                                         allowretry = 0;
2188                                                         /* see if we need to get rid of the conference */
2189                                                         AST_LIST_LOCK(&confs);
2190                                                         cnf->refcount--;
2191                                                         if (!cnf->refcount) {
2192                                                                 conf_free(cnf);
2193                                                         }
2194                                                         AST_LIST_UNLOCK(&confs);
2195                                                         break;
2196                                                 }
2197
2198                                                 /* Don't retry pin with a static pin */
2199                                                 if (*the_pin && (always_prompt==0)) {
2200                                                         break;
2201                                                 }
2202                                         }
2203                                 } else {
2204                                         /* No pin required */
2205                                         allowretry = 0;
2206
2207                                         /* Run the conference */
2208                                         res = conf_run(chan, cnf, confflags.flags);
2209                                 }
2210                         }
2211                 }
2212         } while (allowretry);
2213         
2214         LOCAL_USER_REMOVE(u);
2215         
2216         return res;
2217 }
2218
2219 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident) 
2220 {
2221         struct ast_conf_user *user = NULL;
2222         int cid;
2223         
2224         sscanf(callerident, "%i", &cid);
2225         if (conf && callerident) {
2226                 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2227                         if (cid == user->user_no)
2228                                 return user;
2229                 }
2230         }
2231         return NULL;
2232 }
2233
2234 /*! \brief The MeetMeadmin application */
2235 /* MeetMeAdmin(confno, command, caller) */
2236 static int admin_exec(struct ast_channel *chan, void *data) {
2237         char *params;
2238         struct ast_conference *cnf;
2239         struct ast_conf_user *user = NULL;
2240         struct localuser *u;
2241         AST_DECLARE_APP_ARGS(args,
2242                 AST_APP_ARG(confno);
2243                 AST_APP_ARG(command);
2244                 AST_APP_ARG(user);
2245         );
2246         
2247         LOCAL_USER_ADD(u);
2248
2249         AST_LIST_LOCK(&confs);
2250         /* The param has the conference number the user and the command to execute */
2251         if (!ast_strlen_zero(data)) {           
2252                 params = ast_strdupa((char *) data);
2253
2254                 AST_STANDARD_APP_ARGS(args, params);
2255
2256                 if (!args.command) {
2257                         ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
2258                         AST_LIST_UNLOCK(&confs);
2259                         LOCAL_USER_REMOVE(u);
2260                         return -1;
2261                 }
2262                 AST_LIST_TRAVERSE(&confs, cnf, list) {
2263                         if (!strcmp(cnf->confno, args.confno))
2264                                 break;
2265                 }
2266                 
2267                 if (args.user)
2268                         user = find_user(cnf, args.user);
2269                 
2270                 if (cnf) {
2271                         switch((int) (*args.command)) {
2272                         case 76: /* L: Lock */ 
2273                                 cnf->locked = 1;
2274                                 break;
2275                         case 108: /* l: Unlock */ 
2276                                 cnf->locked = 0;
2277                                 break;
2278                         case 75: /* K: kick all users */
2279                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2280                                         user->adminflags |= ADMINFLAG_KICKME;
2281                                 break;
2282                         case 101: /* e: Eject last user*/
2283                                 user = AST_LIST_LAST(&cnf->userlist);
2284                                 if (!(user->userflags & CONFFLAG_ADMIN))
2285                                         user->adminflags |= ADMINFLAG_KICKME;
2286                                 else
2287                                         ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
2288                                 break;
2289                         case 77: /* M: Mute */ 
2290                                 if (user) {
2291                                         user->adminflags |= ADMINFLAG_MUTED;
2292                                 } else
2293                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2294                                 break;
2295                         case 78: /* N: Mute all (non-admin) users */
2296                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
2297                                         if (!(user->userflags & CONFFLAG_ADMIN))
2298                                                 user->adminflags |= ADMINFLAG_MUTED;
2299                                 }
2300                                 break;                                  
2301                         case 109: /* m: Unmute */ 
2302                                 if (user) {
2303                                         user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2304                                 } else
2305                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2306                                 break;
2307                         case 110: /* n: Unmute all users */
2308                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2309                                         user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2310                                 break;
2311                         case 107: /* k: Kick user */ 
2312                                 if (user)
2313                                         user->adminflags |= ADMINFLAG_KICKME;
2314                                 else
2315                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2316                                 break;
2317                         case 118: /* v: Lower all users listen volume */
2318                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2319                                         tweak_listen_volume(user, VOL_DOWN);
2320                                 break;
2321                         case 86: /* V: Raise all users listen volume */
2322                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2323                                         tweak_listen_volume(user, VOL_UP);
2324                                 break;
2325                         case 115: /* s: Lower all users speaking volume */
2326                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2327                                         tweak_talk_volume(user, VOL_DOWN);
2328                                 break;
2329                         case 83: /* S: Raise all users speaking volume */
2330                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2331                                         tweak_talk_volume(user, VOL_UP);
2332                                 break;
2333                         case 82: /* R: Reset all volume levels */
2334                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2335                                         reset_volumes(user);
2336                                 break;
2337                         case 114: /* r: Reset user's volume level */
2338                                 if (user)
2339                                         reset_volumes(user);
2340                                 else
2341                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2342                                 break;
2343                         case 85: /* U: Raise user's listen volume */
2344                                 if (user)
2345                                         tweak_listen_volume(user, VOL_UP);
2346                                 else
2347                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2348                                 break;
2349                         case 117: /* u: Lower user's listen volume */
2350                                 if (user)
2351                                         tweak_listen_volume(user, VOL_DOWN);
2352                                 else
2353                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2354                                 break;
2355                         case 84: /* T: Raise user's talk volume */
2356                                 if (user)
2357                                         tweak_talk_volume(user, VOL_UP);
2358                                 else
2359                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2360                                 break;
2361                         case 116: /* t: Lower user's talk volume */
2362                                 if (user) 
2363                                         tweak_talk_volume(user, VOL_DOWN);
2364                                 else 
2365                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2366                                 break;
2367                         }
2368                 } else {
2369                         ast_log(LOG_NOTICE, "Conference Number not found\n");
2370                 }
2371         }
2372         AST_LIST_UNLOCK(&confs);
2373
2374         LOCAL_USER_REMOVE(u);
2375         
2376         return 0;
2377 }
2378
2379 static int meetmemute(struct mansession *s, struct message *m, int mute)
2380 {
2381         struct ast_conference *conf;
2382         struct ast_conf_user *user;
2383         char *confid = astman_get_header(m, "Meetme");
2384         char *userid = astman_get_header(m, "Usernum");
2385         int userno;
2386
2387         if (ast_strlen_zero(confid)) {
2388                 astman_send_error(s, m, "Meetme conference not specified");
2389                 return 0;
2390         }
2391
2392         if (ast_strlen_zero(userid)) {
2393                 astman_send_error(s, m, "Meetme user number not specified");
2394                 return 0;
2395         }
2396
2397         userno = strtoul(userid, &userid, 10);
2398
2399         if (*userid) {
2400                 astman_send_error(s, m, "Invalid user number");
2401                 return 0;
2402         }
2403
2404         /* Look in the conference list */
2405         AST_LIST_LOCK(&confs);
2406         AST_LIST_TRAVERSE(&confs, conf, list) {
2407                 if (!strcmp(confid, conf->confno))
2408                         break;
2409         }
2410
2411         if (!conf) {
2412                 AST_LIST_UNLOCK(&confs);
2413                 astman_send_error(s, m, "Meetme conference does not exist");
2414                 return 0;
2415         }
2416
2417         AST_LIST_TRAVERSE(&conf->userlist, user, list)
2418                 if (user->user_no == userno)
2419                         break;
2420
2421         if (!user) {
2422                 AST_LIST_UNLOCK(&confs);
2423                 astman_send_error(s, m, "User number not found");
2424                 return 0;
2425         }
2426
2427         if (mute)
2428                 user->adminflags |= ADMINFLAG_MUTED;    /* request user muting */
2429         else
2430                 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);   /* request user unmuting */
2431
2432         AST_LIST_UNLOCK(&confs);
2433
2434         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);
2435
2436         astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
2437         return 0;
2438 }
2439
2440 static int action_meetmemute(struct mansession *s, struct message *m)
2441 {
2442         return meetmemute(s, m, 1);
2443 }
2444
2445 static int action_meetmeunmute(struct mansession *s, struct message *m)
2446 {
2447         return meetmemute(s, m, 0);
2448 }
2449
2450 static void *recordthread(void *args)
2451 {
2452         struct ast_conference *cnf = args;
2453         struct ast_frame *f=NULL;
2454         int flags;
2455         struct ast_filestream *s=NULL;
2456         int res=0;
2457         int x;
2458         const char *oldrecordingfilename = NULL;
2459
2460         if (!cnf || !cnf->lchan) {
2461                 pthread_exit(0);
2462         }
2463
2464         ast_stopstream(cnf->lchan);
2465         flags = O_CREAT|O_TRUNC|O_WRONLY;
2466
2467
2468         cnf->recording = MEETME_RECORD_ACTIVE;
2469         while (ast_waitfor(cnf->lchan, -1) > -1) {
2470                 if (cnf->recording == MEETME_RECORD_TERMINATE) {
2471                         AST_LIST_LOCK(&confs);
2472                         AST_LIST_UNLOCK(&confs);
2473                         break;
2474                 }
2475                 if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
2476                         s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
2477                         oldrecordingfilename = cnf->recordingfilename;
2478                 }
2479                 
2480                 f = ast_read(cnf->lchan);
2481                 if (!f) {
2482                         res = -1;
2483                         break;
2484                 }
2485                 if (f->frametype == AST_FRAME_VOICE) {
2486                         ast_mutex_lock(&cnf->listenlock);
2487                         for (x=0;x<AST_FRAME_BITS;x++) {
2488                                 /* Free any translations that have occured */
2489                                 if (cnf->transframe[x]) {
2490                                         ast_frfree(cnf->transframe[x]);
2491                                         cnf->transframe[x] = NULL;
2492                                 }
2493                         }
2494                         if (cnf->origframe)
2495                                 ast_frfree(cnf->origframe);
2496                         cnf->origframe = f;
2497                         ast_mutex_unlock(&cnf->listenlock);
2498                         if (s)
2499                                 res = ast_writestream(s, f);
2500                         if (res) {
2501                                 ast_frfree(f);
2502                                 break;
2503                         }
2504                 }
2505                 ast_frfree(f);
2506         }
2507         cnf->recording = MEETME_RECORD_OFF;
2508         if (s)
2509                 ast_closestream(s);
2510         
2511         pthread_exit(0);
2512 }
2513
2514 static void load_config(void)
2515 {
2516         struct ast_config *cfg;
2517         char *val;
2518
2519         audio_buffers = DEFAULT_AUDIO_BUFFERS;
2520
2521         if (!(cfg = ast_config_load(CONFIG_FILE_NAME)))
2522                 return;
2523
2524         if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
2525                 if ((sscanf(val, "%d", &audio_buffers) != 1)) {
2526                         ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
2527                         audio_buffers = DEFAULT_AUDIO_BUFFERS;
2528                 } else if ((audio_buffers < ZT_DEFAULT_NUM_BUFS) || (audio_buffers > ZT_MAX_NUM_BUFS)) {
2529                         ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
2530                                 ZT_DEFAULT_NUM_BUFS, ZT_MAX_NUM_BUFS);
2531                         audio_buffers = DEFAULT_AUDIO_BUFFERS;
2532                 }
2533                 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
2534                         ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
2535         }
2536
2537         ast_config_destroy(cfg);
2538 }
2539
2540 static int unload_module(void *mod)
2541 {
2542         int res;
2543         
2544         res = ast_cli_unregister(&cli_conf);
2545         res |= ast_manager_unregister("MeetmeMute");
2546         res |= ast_manager_unregister("MeetmeUnmute");
2547         res |= ast_unregister_application(app3);
2548         res |= ast_unregister_application(app2);
2549         res |= ast_unregister_application(app);
2550
2551         STANDARD_HANGUP_LOCALUSERS;
2552
2553         return res;
2554 }
2555
2556 static int load_module(void *mod)
2557 {
2558         int res;
2559
2560         load_config();
2561
2562         res = ast_cli_register(&cli_conf);
2563         res |= ast_manager_register("MeetmeMute", EVENT_FLAG_CALL, action_meetmemute, "Mute a Meetme user");
2564         res |= ast_manager_register("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute, "Unmute a Meetme user");
2565         res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
2566         res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
2567         res |= ast_register_application(app, conf_exec, synopsis, descrip);
2568
2569         return res;
2570 }
2571
2572 static int reload(void *mod)
2573 {
2574         load_config();
2575
2576         return 0;
2577 }
2578
2579 static const char *description(void)
2580 {
2581         return "MeetMe conference bridge";
2582 }
2583
2584 static const char *key(void)
2585 {
2586         return ASTERISK_GPL_KEY;
2587 }
2588
2589 STD_MOD(MOD_1, reload, NULL, NULL);
2590