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