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