fix up some places where frames are not free'd and remove an unnecessary
[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                                                 ast_frfree(f);
1444                                                 break;
1445                                         } else if (option_debug > 1)
1446                                                 ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
1447                                 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
1448                                         ret = 0;
1449                                         ast_frfree(f);
1450                                         break;
1451                                 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
1452                                         if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
1453                                                 ast_log(LOG_WARNING, "Error setting conference\n");
1454                                                 close(fd);
1455                                                 ast_frfree(f);
1456                                                 goto outrun;
1457                                         }
1458
1459                                         /* if we are entering the menu, and the user has a channel-driver
1460                                            volume adjustment, clear it
1461                                         */
1462                                         if (!menu_active && user->talk.desired && !user->talk.actual)
1463                                                 set_talk_volume(user, 0);
1464
1465                                         if (musiconhold) {
1466                                                 ast_moh_stop(chan);
1467                                         }
1468                                         if ((confflags & CONFFLAG_ADMIN)) {
1469                                                 /* Admin menu */
1470                                                 if (!menu_active) {
1471                                                         menu_active = 1;
1472                                                         /* Record this sound! */
1473                                                         if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
1474                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1475                                                                 ast_stopstream(chan);
1476                                                         } else 
1477                                                                 dtmf = 0;
1478                                                 } else 
1479                                                         dtmf = f->subclass;
1480                                                 if (dtmf) {
1481                                                         switch(dtmf) {
1482                                                         case '1': /* Un/Mute */
1483                                                                 menu_active = 0;
1484
1485                                                                 /* for admin, change both admin and use flags */
1486                                                                 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
1487                                                                         user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
1488                                                                 else
1489                                                                         user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
1490
1491                                                                 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
1492                                                                         if (!ast_streamfile(chan, "conf-muted", chan->language))
1493                                                                                 ast_waitstream(chan, "");
1494                                                                 } else {
1495                                                                         if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1496                                                                                 ast_waitstream(chan, "");
1497                                                                 }
1498                                                                 break;
1499                                                         case '2': /* Un/Lock the Conference */
1500                                                                 menu_active = 0;
1501                                                                 if (conf->locked) {
1502                                                                         conf->locked = 0;
1503                                                                         if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1504                                                                                 ast_waitstream(chan, "");
1505                                                                 } else {
1506                                                                         conf->locked = 1;
1507                                                                         if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1508                                                                                 ast_waitstream(chan, "");
1509                                                                 }
1510                                                                 break;
1511                                                         case '3': /* Eject last user */
1512                                                                 menu_active = 0;
1513                                                                 usr = AST_LIST_LAST(&conf->userlist);
1514                                                                 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1515                                                                         if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1516                                                                                 ast_waitstream(chan, "");
1517                                                                 } else 
1518                                                                         usr->adminflags |= ADMINFLAG_KICKME;
1519                                                                 ast_stopstream(chan);
1520                                                                 break;  
1521                                                         case '4':
1522                                                                 tweak_listen_volume(user, VOL_DOWN);
1523                                                                 break;
1524                                                         case '6':
1525                                                                 tweak_listen_volume(user, VOL_UP);
1526                                                                 break;
1527                                                         case '7':
1528                                                                 tweak_talk_volume(user, VOL_DOWN);
1529                                                                 break;
1530                                                         case '8':
1531                                                                 menu_active = 0;
1532                                                                 break;
1533                                                         case '9':
1534                                                                 tweak_talk_volume(user, VOL_UP);
1535                                                                 break;
1536                                                         default:
1537                                                                 menu_active = 0;
1538                                                                 /* Play an error message! */
1539                                                                 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1540                                                                         ast_waitstream(chan, "");
1541                                                                 break;
1542                                                         }
1543                                                 }
1544                                         } else {
1545                                                 /* User menu */
1546                                                 if (!menu_active) {
1547                                                         menu_active = 1;
1548                                                         if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
1549                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1550                                                                 ast_stopstream(chan);
1551                                                         } else
1552                                                                 dtmf = 0;
1553                                                 } else 
1554                                                         dtmf = f->subclass;
1555                                                 if (dtmf) {
1556                                                         switch(dtmf) {
1557                                                         case '1': /* Un/Mute */
1558                                                                 menu_active = 0;
1559
1560                                                                 /* user can only toggle the self-muted state */
1561                                                                 user->adminflags ^= ADMINFLAG_SELFMUTED;
1562
1563                                                                 /* they can't override the admin mute state */
1564                                                                 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
1565                                                                         if (!ast_streamfile(chan, "conf-muted", chan->language))
1566                                                                                 ast_waitstream(chan, "");
1567                                                                 } else {
1568                                                                         if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1569                                                                                 ast_waitstream(chan, "");
1570                                                                 }
1571                                                                 break;
1572                                                         case '4':
1573                                                                 tweak_listen_volume(user, VOL_DOWN);
1574                                                                 break;
1575                                                         case '6':
1576                                                                 tweak_listen_volume(user, VOL_UP);
1577                                                                 break;
1578                                                         case '7':
1579                                                                 tweak_talk_volume(user, VOL_DOWN);
1580                                                                 break;
1581                                                         case '8':
1582                                                                 menu_active = 0;
1583                                                                 break;
1584                                                         case '9':
1585                                                                 tweak_talk_volume(user, VOL_UP);
1586                                                                 break;
1587                                                         default:
1588                                                                 menu_active = 0;
1589                                                                 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1590                                                                         ast_waitstream(chan, "");
1591                                                                 break;
1592                                                         }
1593                                                 }
1594                                         }
1595                                         if (musiconhold)
1596                                                 ast_moh_start(chan, NULL);
1597
1598                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1599                                                 ast_log(LOG_WARNING, "Error setting conference\n");
1600                                                 close(fd);
1601                                                 ast_frfree(f);
1602                                                 goto outrun;
1603                                         }
1604
1605                                         conf_flush(fd, chan);
1606                                 } else if (option_debug) {
1607                                         ast_log(LOG_DEBUG,
1608                                                 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
1609                                                 chan->name, f->frametype, f->subclass);
1610                                 }
1611                                 ast_frfree(f);
1612                         } else if (outfd > -1) {
1613                                 res = read(outfd, buf, CONF_SIZE);
1614                                 if (res > 0) {
1615                                         memset(&fr, 0, sizeof(fr));
1616                                         fr.frametype = AST_FRAME_VOICE;
1617                                         fr.subclass = AST_FORMAT_SLINEAR;
1618                                         fr.datalen = res;
1619                                         fr.samples = res/2;
1620                                         fr.data = buf;
1621                                         fr.offset = AST_FRIENDLY_OFFSET;
1622                                         if (!user->listen.actual && 
1623                                                 ((confflags & CONFFLAG_MONITOR) || 
1624                                                  (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
1625                                                  (!user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
1626                                                  )) {
1627                                                 int index;
1628                                                 for (index=0;index<AST_FRAME_BITS;index++)
1629                                                         if (chan->rawwriteformat & (1 << index))
1630                                                                 break;
1631                                                 if (index >= AST_FRAME_BITS)
1632                                                         goto bailoutandtrynormal;
1633                                                 ast_mutex_lock(&conf->listenlock);
1634                                                 if (!conf->transframe[index]) {
1635                                                         if (conf->origframe) {
1636                                                                 if (!conf->transpath[index])
1637                                                                         conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
1638                                                                 if (conf->transpath[index]) {
1639                                                                         conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
1640                                                                         if (!conf->transframe[index])
1641                                                                                 conf->transframe[index] = &ast_null_frame;
1642                                                                 }
1643                                                         }
1644                                                 }
1645                                                 if (conf->transframe[index]) {
1646                                                         if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
1647                                                                 if (ast_write(chan, conf->transframe[index]))
1648                                                                         ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1649                                                         }
1650                                                 } else {
1651                                                         ast_mutex_unlock(&conf->listenlock);
1652                                                         goto bailoutandtrynormal;
1653                                                 }
1654                                                 ast_mutex_unlock(&conf->listenlock);
1655                                         } else {
1656 bailoutandtrynormal:                                    
1657                                                 if (user->listen.actual)
1658                                                         ast_frame_adjust_volume(&fr, user->listen.actual);
1659                                                 if (ast_write(chan, &fr) < 0) {
1660                                                         ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1661                                                 }
1662                                         }
1663                                 } else 
1664                                         ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1665                         }
1666                         lastmarked = currentmarked;
1667                 }
1668         }
1669
1670         if (musiconhold)
1671                 ast_moh_stop(chan);
1672         
1673         if (using_pseudo)
1674                 close(fd);
1675         else {
1676                 /* Take out of conference */
1677                 ztc.chan = 0;   
1678                 ztc.confno = 0;
1679                 ztc.confmode = 0;
1680                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1681                         ast_log(LOG_WARNING, "Error setting conference\n");
1682                 }
1683         }
1684
1685         reset_volumes(user);
1686
1687         AST_LIST_LOCK(&confs);
1688         if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1689                 conf_play(chan, conf, LEAVE);
1690
1691         if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1692                 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
1693                         if ((conf->chan) && (conf->users > 1)) {
1694                                 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1695                                         ast_waitstream(conf->chan, "");
1696                                 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1697                                         ast_waitstream(conf->chan, "");
1698                         }
1699                         ast_filedelete(user->namerecloc, NULL);
1700                 }
1701         }
1702         AST_LIST_UNLOCK(&confs);
1703
1704  outrun:
1705         AST_LIST_LOCK(&confs);
1706
1707         if (dsp)
1708                 ast_dsp_free(dsp);
1709         
1710         if (user->user_no) { /* Only cleanup users who really joined! */
1711                 now = time(NULL);
1712                 hr = (now - user->jointime) / 3600;
1713                 min = ((now - user->jointime) % 3600) / 60;
1714                 sec = (now - user->jointime) % 60;
1715
1716                 if (sent_event) {
1717                         manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
1718                                       "Channel: %s\r\n"
1719                                       "Uniqueid: %s\r\n"
1720                                       "Meetme: %s\r\n"
1721                                       "Usernum: %d\r\n"
1722                                       "CallerIDnum: %s\r\n"
1723                                       "CallerIDname: %s\r\n"
1724                                       "Duration: %ld\r\n",
1725                                       chan->name, chan->uniqueid, conf->confno, 
1726                                       user->user_no,
1727                                       S_OR(user->chan->cid.cid_num, "<unknown>"),
1728                                       S_OR(user->chan->cid.cid_name, "<unknown>"),
1729                                       (now - user->jointime));
1730                 }
1731
1732                 conf->users--;
1733                 conf->refcount--;
1734                 /* Update table */
1735                 snprintf(members, sizeof(members), "%d", conf->users);
1736                 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
1737                 if (confflags & CONFFLAG_MARKEDUSER) 
1738                         conf->markedusers--;
1739                 /* Remove ourselves from the list */
1740                 AST_LIST_REMOVE(&conf->userlist, user, list);
1741                 if (AST_LIST_EMPTY(&conf->userlist)) {
1742                         /* close this one when no more users and no references*/
1743                         if (!conf->refcount)
1744                                 conf_free(conf);
1745                 }
1746                 /* Return the number of seconds the user was in the conf */
1747                 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
1748                 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1749         }
1750         free(user);
1751         AST_LIST_UNLOCK(&confs);
1752
1753         return ret;
1754 }
1755
1756 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
1757                                                  char *dynamic_pin, int refcount, struct ast_flags *confflags)
1758 {
1759         struct ast_variable *var;
1760         struct ast_conference *cnf;
1761
1762         /* Check first in the conference list */
1763         AST_LIST_LOCK(&confs);
1764         AST_LIST_TRAVERSE(&confs, cnf, list) {
1765                 if (!strcmp(confno, cnf->confno)) 
1766                         break;
1767         }
1768         if (cnf){
1769                 cnf->refcount += refcount;
1770         }
1771         AST_LIST_UNLOCK(&confs);
1772
1773         if (!cnf) {
1774                 char *pin = NULL, *pinadmin = NULL; /* For temp use */
1775
1776                 cnf = ast_calloc(1, sizeof(struct ast_conference));
1777                 if (!cnf) {
1778                         ast_log(LOG_ERROR, "Out of memory\n");
1779                         return NULL;
1780                 }
1781
1782                 var = ast_load_realtime("meetme", "confno", confno, NULL);
1783                 while (var) {
1784                         if (!strcasecmp(var->name, "confno")) {
1785                                 ast_copy_string(cnf->confno, var->value, sizeof(cnf->confno));
1786                         } else if (!strcasecmp(var->name, "pin")) {
1787                                 pin = ast_strdupa(var->value);
1788                         } else if (!strcasecmp(var->name, "adminpin")) {
1789                                 pinadmin = ast_strdupa(var->value);
1790                         }
1791                         var = var->next;
1792                 }
1793                 ast_variables_destroy(var);
1794
1795                 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount);
1796         }
1797
1798         if (cnf) {
1799                 if (confflags && !cnf->chan &&
1800                     !ast_test_flag(confflags, CONFFLAG_QUIET) &&
1801                     ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
1802                         ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
1803                         ast_clear_flag(confflags, CONFFLAG_INTROUSER);
1804                 }
1805                 
1806                 if (confflags && !cnf->chan &&
1807                     ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
1808                         ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
1809                         ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
1810                 }
1811         }
1812
1813         return cnf;
1814 }
1815
1816
1817 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
1818                                         char *dynamic_pin, int refcount, struct ast_flags *confflags)
1819 {
1820         struct ast_config *cfg;
1821         struct ast_variable *var;
1822         struct ast_conference *cnf;
1823         char *parse;
1824         AST_DECLARE_APP_ARGS(args,
1825                 AST_APP_ARG(confno);
1826                 AST_APP_ARG(pin);
1827                 AST_APP_ARG(pinadmin);
1828         );
1829
1830         /* Check first in the conference list */
1831         AST_LIST_LOCK(&confs);
1832         AST_LIST_TRAVERSE(&confs, cnf, list) {
1833                 if (!strcmp(confno, cnf->confno)) 
1834                         break;
1835         }
1836         if (cnf){
1837                 cnf->refcount += refcount;
1838         }
1839         AST_LIST_UNLOCK(&confs);
1840
1841         if (!cnf) {
1842                 if (dynamic) {
1843                         /* No need to parse meetme.conf */
1844                         ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1845                         if (dynamic_pin) {
1846                                 if (dynamic_pin[0] == 'q') {
1847                                         /* Query the user to enter a PIN */
1848                                         if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0) < 0)
1849                                                 return NULL;
1850                                 }
1851                                 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount);
1852                         } else {
1853                                 cnf = build_conf(confno, "", "", make, dynamic, refcount);
1854                         }
1855                 } else {
1856                         /* Check the config */
1857                         cfg = ast_config_load(CONFIG_FILE_NAME);
1858                         if (!cfg) {
1859                                 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
1860                                 return NULL;
1861                         }
1862                         for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
1863                                 if (strcasecmp(var->name, "conf"))
1864                                         continue;
1865                                 
1866                                 if (!(parse = ast_strdupa(var->value)))
1867                                         return NULL;
1868                                 
1869                                 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
1870                                 if (!strcasecmp(args.confno, confno)) {
1871                                         /* Bingo it's a valid conference */
1872                                         cnf = build_conf(args.confno,
1873                                                         S_OR(args.pin, ""),
1874                                                         S_OR(args.pinadmin, ""),
1875                                                         make, dynamic, refcount);
1876                                         break;
1877                                 }
1878                         }
1879                         if (!var) {
1880                                 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1881                         }
1882                         ast_config_destroy(cfg);
1883                 }
1884         } else if (dynamic_pin) {
1885                 /* Correct for the user selecting 'D' instead of 'd' to have
1886                    someone join into a conference that has already been created
1887                    with a pin. */
1888                 if (dynamic_pin[0] == 'q')
1889                         dynamic_pin[0] = '\0';
1890         }
1891
1892         if (cnf) {
1893                 if (confflags && !cnf->chan &&
1894                     !ast_test_flag(confflags, CONFFLAG_QUIET) &&
1895                     ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
1896                         ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
1897                         ast_clear_flag(confflags, CONFFLAG_INTROUSER);
1898                 }
1899                 
1900                 if (confflags && !cnf->chan &&
1901                     ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
1902                         ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
1903                         ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
1904                 }
1905         }
1906
1907         return cnf;
1908 }
1909
1910 /*! \brief The MeetmeCount application */
1911 static int count_exec(struct ast_channel *chan, void *data)
1912 {
1913         struct localuser *u;
1914         int res = 0;
1915         struct ast_conference *conf;
1916         int count;
1917         char *localdata;
1918         char val[80] = "0"; 
1919         AST_DECLARE_APP_ARGS(args,
1920                 AST_APP_ARG(confno);
1921                 AST_APP_ARG(varname);
1922         );
1923
1924         if (ast_strlen_zero(data)) {
1925                 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1926                 return -1;
1927         }
1928
1929         LOCAL_USER_ADD(u);
1930         
1931         if (!(localdata = ast_strdupa(data))) {
1932                 LOCAL_USER_REMOVE(u);
1933                 return -1;
1934         }
1935
1936         AST_STANDARD_APP_ARGS(args, localdata);
1937         
1938         conf = find_conf(chan, args.confno, 0, 0, NULL, 0, NULL);
1939
1940         if (conf)
1941                 count = conf->users;
1942         else
1943                 count = 0;
1944
1945         if (!ast_strlen_zero(args.varname)){
1946                 /* have var so load it and exit */
1947                 snprintf(val, sizeof(val), "%d",count);
1948                 pbx_builtin_setvar_helper(chan, args.varname, val);
1949         } else {
1950                 if (chan->_state != AST_STATE_UP)
1951                         ast_answer(chan);
1952                 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1953         }
1954         LOCAL_USER_REMOVE(u);
1955
1956         return res;
1957 }
1958
1959 /*! \brief The meetme() application */
1960 static int conf_exec(struct ast_channel *chan, void *data)
1961 {
1962         int res=-1;
1963         struct localuser *u;
1964         char confno[AST_MAX_EXTENSION] = "";
1965         int allowretry = 0;
1966         int retrycnt = 0;
1967         struct ast_conference *cnf;
1968         struct ast_flags confflags = {0};
1969         int dynamic = 0;
1970         int empty = 0, empty_no_pin = 0;
1971         int always_prompt = 0;
1972         char *notdata, *info, the_pin[AST_MAX_EXTENSION] = "";
1973         AST_DECLARE_APP_ARGS(args,
1974                 AST_APP_ARG(confno);
1975                 AST_APP_ARG(options);
1976                 AST_APP_ARG(pin);
1977         );
1978
1979         LOCAL_USER_ADD(u);
1980
1981         if (ast_strlen_zero(data)) {
1982                 allowretry = 1;
1983                 notdata = "";
1984         } else {
1985                 notdata = data;
1986         }
1987         
1988         if (chan->_state != AST_STATE_UP)
1989                 ast_answer(chan);
1990
1991         info = ast_strdupa(notdata);
1992
1993         AST_STANDARD_APP_ARGS(args, info);      
1994
1995         if (args.confno) {
1996                 ast_copy_string(confno, args.confno, sizeof(confno));
1997                 if (ast_strlen_zero(confno)) {
1998                         allowretry = 1;
1999                 }
2000         }
2001         
2002         if (args.pin)
2003                 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2004
2005         if (args.options) {
2006                 ast_app_parse_options(meetme_opts, &confflags, NULL, args.options);
2007                 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2008                 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2009                         strcpy(the_pin, "q");
2010
2011                 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2012                 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2013                 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2014         }
2015
2016         do {
2017                 if (retrycnt > 3)
2018                         allowretry = 0;
2019                 if (empty) {
2020                         int i, map[1024] = { 0, };
2021                         struct ast_config *cfg;
2022                         struct ast_variable *var;
2023                         int confno_int;
2024
2025                         AST_LIST_LOCK(&confs);
2026                         AST_LIST_TRAVERSE(&confs, cnf, list) {
2027                                 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
2028                                         /* Disqualify in use conference */
2029                                         if (confno_int >= 0 && confno_int < 1024)
2030                                                 map[confno_int]++;
2031                                 }
2032                         }
2033                         AST_LIST_UNLOCK(&confs);
2034
2035                         /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2036                         if ((empty_no_pin) || (!dynamic)) {
2037                                 cfg = ast_config_load(CONFIG_FILE_NAME);
2038                                 if (cfg) {
2039                                         var = ast_variable_browse(cfg, "rooms");
2040                                         while (var) {
2041                                                 if (!strcasecmp(var->name, "conf")) {
2042                                                         char *stringp = ast_strdupa(var->value);
2043                                                         if (stringp) {
2044                                                                 char *confno_tmp = strsep(&stringp, "|,");
2045                                                                 int found = 0;
2046                                                                 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
2047                                                                         if ((confno_int >= 0) && (confno_int < 1024)) {
2048                                                                                 if (stringp && empty_no_pin) {
2049                                                                                         map[confno_int]++;
2050                                                                                 }
2051                                                                         }
2052                                                                 }
2053                                                                 if (!dynamic) {
2054                                                                         /* For static:  run through the list and see if this conference is empty */
2055                                                                         AST_LIST_LOCK(&confs);
2056                                                                         AST_LIST_TRAVERSE(&confs, cnf, list) {
2057                                                                                 if (!strcmp(confno_tmp, cnf->confno)) {
2058                                                                                         /* The conference exists, therefore it's not empty */
2059                                                                                         found = 1;
2060                                                                                         break;
2061                                                                                 }
2062                                                                         }
2063                                                                         AST_LIST_UNLOCK(&confs);
2064                                                                         if (!found) {
2065                                                                                 /* At this point, we have a confno_tmp (static conference) that is empty */
2066                                                                                 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2067                                                                                         /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
2068                                                                                          * Case 2:  empty_no_pin and pin is blank (but not NULL)
2069                                                                                          * Case 3:  not empty_no_pin
2070                                                                                          */
2071                                                                                         ast_copy_string(confno, confno_tmp, sizeof(confno));
2072                                                                                         break;
2073                                                                                         /* XXX the map is not complete (but we do have a confno) */
2074                                                                                 }
2075                                                                         }
2076                                                                 }
2077                                                         }
2078                                                 }
2079                                                 var = var->next;
2080                                         }
2081                                         ast_config_destroy(cfg);
2082                                 }
2083                         }
2084
2085                         /* Select first conference number not in use */
2086                         if (ast_strlen_zero(confno) && dynamic) {
2087                                 for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
2088                                         if (!map[i]) {
2089                                                 snprintf(confno, sizeof(confno), "%d", i);
2090                                                 break;
2091                                         }
2092                                 }
2093                         }
2094
2095                         /* Not found? */
2096                         if (ast_strlen_zero(confno)) {
2097                                 res = ast_streamfile(chan, "conf-noempty", chan->language);
2098                                 if (!res)
2099                                         ast_waitstream(chan, "");
2100                         } else {
2101                                 if (sscanf(confno, "%d", &confno_int) == 1) {
2102                                         res = ast_streamfile(chan, "conf-enteringno", chan->language);
2103                                         if (!res) {
2104                                                 ast_waitstream(chan, "");
2105                                                 res = ast_say_digits(chan, confno_int, "", chan->language);
2106                                         }
2107                                 } else {
2108                                         ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2109                                 }
2110                         }
2111                 }
2112
2113                 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2114                         /* Prompt user for conference number */
2115                         res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2116                         if (res < 0) {
2117                                 /* Don't try to validate when we catch an error */
2118                                 confno[0] = '\0';
2119                                 allowretry = 0;
2120                                 break;
2121                         }
2122                 }
2123                 if (!ast_strlen_zero(confno)) {
2124                         /* Check the validity of the conference */
2125                         cnf = find_conf(chan, confno, 1, dynamic, the_pin, 1, &confflags);
2126                         if (!cnf)
2127                                 cnf = find_conf_realtime(chan, confno, 1, dynamic, the_pin, 1, &confflags);
2128
2129                         if (!cnf) {
2130                                 res = ast_streamfile(chan, "conf-invalid", chan->language);
2131                                 if (!res)
2132                                         ast_waitstream(chan, "");
2133                                 res = -1;
2134                                 if (allowretry)
2135                                         confno[0] = '\0';
2136                         } else {
2137                                 if ((!ast_strlen_zero(cnf->pin) &&
2138                                      !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2139                                     (!ast_strlen_zero(cnf->pinadmin) &&
2140                                      ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2141                                         char pin[AST_MAX_EXTENSION]="";
2142                                         int j;
2143
2144                                         /* Allow the pin to be retried up to 3 times */
2145                                         for (j = 0; j < 3; j++) {
2146                                                 if (*the_pin && (always_prompt == 0)) {
2147                                                         ast_copy_string(pin, the_pin, sizeof(pin));
2148                                                         res = 0;
2149                                                 } else {
2150                                                         /* Prompt user for pin if pin is required */
2151                                                         res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2152                                                 }
2153                                                 if (res >= 0) {
2154                                                         if (!strcasecmp(pin, cnf->pin) ||
2155                                                             (!ast_strlen_zero(cnf->pinadmin) &&
2156                                                              !strcasecmp(pin, cnf->pinadmin))) {
2157                                                                 /* Pin correct */
2158                                                                 allowretry = 0;
2159                                                                 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) 
2160                                                                         ast_set_flag(&confflags, CONFFLAG_ADMIN);
2161                                                                 /* Run the conference */
2162                                                                 res = conf_run(chan, cnf, confflags.flags);
2163                                                                 break;
2164                                                         } else {
2165                                                                 /* Pin invalid */
2166                                                                 if (!ast_streamfile(chan, "conf-invalidpin", chan->language))
2167                                                                         res = ast_waitstream(chan, AST_DIGIT_ANY);
2168                                                                 else {
2169                                                                         ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
2170                                                                         break;
2171                                                                 }
2172                                                                 if (res < 0) {
2173                                                                         AST_LIST_LOCK(&confs);
2174                                                                         cnf->refcount--;
2175                                                                         if (!cnf->refcount){
2176                                                                                 conf_free(cnf);
2177                                                                         }
2178                                                                         AST_LIST_UNLOCK(&confs);
2179                                                                         break;
2180                                                                 }
2181                                                                 pin[0] = res;
2182                                                                 pin[1] = '\0';
2183                                                                 res = -1;
2184                                                                 if (allowretry)
2185                                                                         confno[0] = '\0';
2186                                                         }
2187                                                 } else {
2188                                                         /* failed when getting the pin */
2189                                                         res = -1;
2190                                                         allowretry = 0;
2191                                                         /* see if we need to get rid of the conference */
2192                                                         AST_LIST_LOCK(&confs);
2193                                                         cnf->refcount--;
2194                                                         if (!cnf->refcount) {
2195                                                                 conf_free(cnf);
2196                                                         }
2197                                                         AST_LIST_UNLOCK(&confs);
2198                                                         break;
2199                                                 }
2200
2201                                                 /* Don't retry pin with a static pin */
2202                                                 if (*the_pin && (always_prompt==0)) {
2203                                                         break;
2204                                                 }
2205                                         }
2206                                 } else {
2207                                         /* No pin required */
2208                                         allowretry = 0;
2209
2210                                         /* Run the conference */
2211                                         res = conf_run(chan, cnf, confflags.flags);
2212                                 }
2213                         }
2214                 }
2215         } while (allowretry);
2216         
2217         LOCAL_USER_REMOVE(u);
2218         
2219         return res;
2220 }
2221
2222 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident) 
2223 {
2224         struct ast_conf_user *user = NULL;
2225         int cid;
2226         
2227         sscanf(callerident, "%i", &cid);
2228         if (conf && callerident) {
2229                 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2230                         if (cid == user->user_no)
2231                                 return user;
2232                 }
2233         }
2234         return NULL;
2235 }
2236
2237 /*! \brief The MeetMeadmin application */
2238 /* MeetMeAdmin(confno, command, caller) */
2239 static int admin_exec(struct ast_channel *chan, void *data) {
2240         char *params;
2241         struct ast_conference *cnf;
2242         struct ast_conf_user *user = NULL;
2243         struct localuser *u;
2244         AST_DECLARE_APP_ARGS(args,
2245                 AST_APP_ARG(confno);
2246                 AST_APP_ARG(command);
2247                 AST_APP_ARG(user);
2248         );
2249         
2250         LOCAL_USER_ADD(u);
2251
2252         AST_LIST_LOCK(&confs);
2253         /* The param has the conference number the user and the command to execute */
2254         if (!ast_strlen_zero(data)) {           
2255                 params = ast_strdupa((char *) data);
2256
2257                 AST_STANDARD_APP_ARGS(args, params);
2258
2259                 if (!args.command) {
2260                         ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
2261                         AST_LIST_UNLOCK(&confs);
2262                         LOCAL_USER_REMOVE(u);
2263                         return -1;
2264                 }
2265                 AST_LIST_TRAVERSE(&confs, cnf, list) {
2266                         if (!strcmp(cnf->confno, args.confno))
2267                                 break;
2268                 }
2269                 
2270                 if (args.user)
2271                         user = find_user(cnf, args.user);
2272                 
2273                 if (cnf) {
2274                         switch((int) (*args.command)) {
2275                         case 76: /* L: Lock */ 
2276                                 cnf->locked = 1;
2277                                 break;
2278                         case 108: /* l: Unlock */ 
2279                                 cnf->locked = 0;
2280                                 break;
2281                         case 75: /* K: kick all users */
2282                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2283                                         user->adminflags |= ADMINFLAG_KICKME;
2284                                 break;
2285                         case 101: /* e: Eject last user*/
2286                                 user = AST_LIST_LAST(&cnf->userlist);
2287                                 if (!(user->userflags & CONFFLAG_ADMIN))
2288                                         user->adminflags |= ADMINFLAG_KICKME;
2289                                 else
2290                                         ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
2291                                 break;
2292                         case 77: /* M: Mute */ 
2293                                 if (user) {
2294                                         user->adminflags |= ADMINFLAG_MUTED;
2295                                 } else
2296                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2297                                 break;
2298                         case 78: /* N: Mute all (non-admin) users */
2299                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
2300                                         if (!(user->userflags & CONFFLAG_ADMIN))
2301                                                 user->adminflags |= ADMINFLAG_MUTED;
2302                                 }
2303                                 break;                                  
2304                         case 109: /* m: Unmute */ 
2305                                 if (user) {
2306                                         user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2307                                 } else
2308                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2309                                 break;
2310                         case 110: /* n: Unmute all users */
2311                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2312                                         user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2313                                 break;
2314                         case 107: /* k: Kick user */ 
2315                                 if (user)
2316                                         user->adminflags |= ADMINFLAG_KICKME;
2317                                 else
2318                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2319                                 break;
2320                         case 118: /* v: Lower all users listen volume */
2321                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2322                                         tweak_listen_volume(user, VOL_DOWN);
2323                                 break;
2324                         case 86: /* V: Raise all users listen volume */
2325                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2326                                         tweak_listen_volume(user, VOL_UP);
2327                                 break;
2328                         case 115: /* s: Lower all users speaking volume */
2329                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2330                                         tweak_talk_volume(user, VOL_DOWN);
2331                                 break;
2332                         case 83: /* S: Raise all users speaking volume */
2333                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2334                                         tweak_talk_volume(user, VOL_UP);
2335                                 break;
2336                         case 82: /* R: Reset all volume levels */
2337                                 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2338                                         reset_volumes(user);
2339                                 break;
2340                         case 114: /* r: Reset user's volume level */
2341                                 if (user)
2342                                         reset_volumes(user);
2343                                 else
2344                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2345                                 break;
2346                         case 85: /* U: Raise user's listen volume */
2347                                 if (user)
2348                                         tweak_listen_volume(user, VOL_UP);
2349                                 else
2350                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2351                                 break;
2352                         case 117: /* u: Lower user's listen volume */
2353                                 if (user)
2354                                         tweak_listen_volume(user, VOL_DOWN);
2355                                 else
2356                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2357                                 break;
2358                         case 84: /* T: Raise user's talk volume */
2359                                 if (user)
2360                                         tweak_talk_volume(user, VOL_UP);
2361                                 else
2362                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2363                                 break;
2364                         case 116: /* t: Lower user's talk volume */
2365                                 if (user) 
2366                                         tweak_talk_volume(user, VOL_DOWN);
2367                                 else 
2368                                         ast_log(LOG_NOTICE, "Specified User not found!\n");
2369                                 break;
2370                         }
2371                 } else {
2372                         ast_log(LOG_NOTICE, "Conference Number not found\n");
2373                 }
2374         }
2375         AST_LIST_UNLOCK(&confs);
2376
2377         LOCAL_USER_REMOVE(u);
2378         
2379         return 0;
2380 }
2381
2382 static int meetmemute(struct mansession *s, struct message *m, int mute)
2383 {
2384         struct ast_conference *conf;
2385         struct ast_conf_user *user;
2386         char *confid = astman_get_header(m, "Meetme");
2387         char *userid = astman_get_header(m, "Usernum");
2388         int userno;
2389
2390         if (ast_strlen_zero(confid)) {
2391                 astman_send_error(s, m, "Meetme conference not specified");
2392                 return 0;
2393         }
2394
2395         if (ast_strlen_zero(userid)) {
2396                 astman_send_error(s, m, "Meetme user number not specified");
2397                 return 0;
2398         }
2399
2400         userno = strtoul(userid, &userid, 10);
2401
2402         if (*userid) {
2403                 astman_send_error(s, m, "Invalid user number");
2404                 return 0;
2405         }
2406
2407         /* Look in the conference list */
2408         AST_LIST_LOCK(&confs);
2409         AST_LIST_TRAVERSE(&confs, conf, list) {
2410                 if (!strcmp(confid, conf->confno))
2411                         break;
2412         }
2413
2414         if (!conf) {
2415                 AST_LIST_UNLOCK(&confs);
2416                 astman_send_error(s, m, "Meetme conference does not exist");
2417                 return 0;
2418         }
2419
2420         AST_LIST_TRAVERSE(&conf->userlist, user, list)
2421                 if (user->user_no == userno)
2422                         break;
2423
2424         if (!user) {
2425                 AST_LIST_UNLOCK(&confs);
2426                 astman_send_error(s, m, "User number not found");
2427                 return 0;
2428         }
2429
2430         if (mute)
2431                 user->adminflags |= ADMINFLAG_MUTED;    /* request user muting */
2432         else
2433                 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);   /* request user unmuting */
2434
2435         AST_LIST_UNLOCK(&confs);
2436
2437         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);
2438
2439         astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
2440         return 0;
2441 }
2442
2443 static int action_meetmemute(struct mansession *s, struct message *m)
2444 {
2445         return meetmemute(s, m, 1);
2446 }
2447
2448 static int action_meetmeunmute(struct mansession *s, struct message *m)
2449 {
2450         return meetmemute(s, m, 0);
2451 }
2452
2453 static void *recordthread(void *args)
2454 {
2455         struct ast_conference *cnf = args;
2456         struct ast_frame *f=NULL;
2457         int flags;
2458         struct ast_filestream *s=NULL;
2459         int res=0;
2460         int x;
2461         const char *oldrecordingfilename = NULL;
2462
2463         if (!cnf || !cnf->lchan) {
2464                 pthread_exit(0);
2465         }
2466
2467         ast_stopstream(cnf->lchan);
2468         flags = O_CREAT|O_TRUNC|O_WRONLY;
2469
2470
2471         cnf->recording = MEETME_RECORD_ACTIVE;
2472         while (ast_waitfor(cnf->lchan, -1) > -1) {
2473                 if (cnf->recording == MEETME_RECORD_TERMINATE) {
2474                         AST_LIST_LOCK(&confs);
2475                         AST_LIST_UNLOCK(&confs);
2476                         break;
2477                 }
2478                 if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
2479                         s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
2480                         oldrecordingfilename = cnf->recordingfilename;
2481                 }
2482                 
2483                 f = ast_read(cnf->lchan);
2484                 if (!f) {
2485                         res = -1;
2486                         break;
2487                 }
2488                 if (f->frametype == AST_FRAME_VOICE) {
2489                         ast_mutex_lock(&cnf->listenlock);
2490                         for (x=0;x<AST_FRAME_BITS;x++) {
2491                                 /* Free any translations that have occured */
2492                                 if (cnf->transframe[x]) {
2493                                         ast_frfree(cnf->transframe[x]);
2494                                         cnf->transframe[x] = NULL;
2495                                 }
2496                         }
2497                         if (cnf->origframe)
2498                                 ast_frfree(cnf->origframe);
2499                         cnf->origframe = f;
2500                         ast_mutex_unlock(&cnf->listenlock);
2501                         if (s)
2502                                 res = ast_writestream(s, f);
2503                         if (res) {
2504                                 ast_frfree(f);
2505                                 break;
2506                         }
2507                 }
2508                 ast_frfree(f);
2509         }
2510         cnf->recording = MEETME_RECORD_OFF;
2511         if (s)
2512                 ast_closestream(s);
2513         
2514         pthread_exit(0);
2515 }
2516
2517 static void load_config(void)
2518 {
2519         struct ast_config *cfg;
2520         char *val;
2521
2522         audio_buffers = DEFAULT_AUDIO_BUFFERS;
2523
2524         if (!(cfg = ast_config_load(CONFIG_FILE_NAME)))
2525                 return;
2526
2527         if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
2528                 if ((sscanf(val, "%d", &audio_buffers) != 1)) {
2529                         ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
2530                         audio_buffers = DEFAULT_AUDIO_BUFFERS;
2531                 } else if ((audio_buffers < ZT_DEFAULT_NUM_BUFS) || (audio_buffers > ZT_MAX_NUM_BUFS)) {
2532                         ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
2533                                 ZT_DEFAULT_NUM_BUFS, ZT_MAX_NUM_BUFS);
2534                         audio_buffers = DEFAULT_AUDIO_BUFFERS;
2535                 }
2536                 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
2537                         ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
2538         }
2539
2540         ast_config_destroy(cfg);
2541 }
2542
2543 static int unload_module(void *mod)
2544 {
2545         int res;
2546         
2547         res = ast_cli_unregister(&cli_conf);
2548         res |= ast_manager_unregister("MeetmeMute");
2549         res |= ast_manager_unregister("MeetmeUnmute");
2550         res |= ast_unregister_application(app3);
2551         res |= ast_unregister_application(app2);
2552         res |= ast_unregister_application(app);
2553
2554         STANDARD_HANGUP_LOCALUSERS;
2555
2556         return res;
2557 }
2558
2559 static int load_module(void *mod)
2560 {
2561         int res;
2562
2563         load_config();
2564
2565         res = ast_cli_register(&cli_conf);
2566         res |= ast_manager_register("MeetmeMute", EVENT_FLAG_CALL, action_meetmemute, "Mute a Meetme user");
2567         res |= ast_manager_register("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute, "Unmute a Meetme user");
2568         res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
2569         res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
2570         res |= ast_register_application(app, conf_exec, synopsis, descrip);
2571
2572         return res;
2573 }
2574
2575 static int reload(void *mod)
2576 {
2577         load_config();
2578
2579         return 0;
2580 }
2581
2582 static const char *description(void)
2583 {
2584         return "MeetMe conference bridge";
2585 }
2586
2587 static const char *key(void)
2588 {
2589         return ASTERISK_GPL_KEY;
2590 }
2591
2592 STD_MOD(MOD_1, reload, NULL, NULL);
2593