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