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