fix moh option in conjunction with intro option (bug #4298)
[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 #: %d  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), "%d", 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, ztc_empty;
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
644         if (confflags & CONFFLAG_MARKEDUSER)
645                 conf->markedusers++;
646       
647         ast_mutex_lock(&conflock);
648         if (conf->firstuser == NULL) {
649                 /* Fill the first new User struct */
650                 user->user_no = 1;
651                 user->nextuser = NULL;
652                 user->prevuser = NULL;
653                 conf->firstuser = user;
654                 conf->lastuser = user;
655         } else {
656                 /* Fill the new user struct */  
657                 user->user_no = conf->lastuser->user_no + 1; 
658                 user->prevuser = conf->lastuser;
659                 user->nextuser = NULL;
660                 if (conf->lastuser->nextuser != NULL) {
661                         ast_log(LOG_WARNING, "Error in User Management!\n");
662                         ast_mutex_unlock(&conflock);
663                         goto outrun;
664                 } else {
665                         conf->lastuser->nextuser = user;
666                         conf->lastuser = user;
667                 }
668         }
669         user->chan = chan;
670         user->userflags = confflags;
671         user->adminflags = 0;
672         user->talking = -1;
673         ast_mutex_unlock(&conflock);
674         origquiet = confflags & CONFFLAG_QUIET;
675         if (confflags & CONFFLAG_EXIT_CONTEXT) {
676                 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) 
677                         strncpy(exitcontext, agifile, sizeof(exitcontext) - 1);
678                 else if (!ast_strlen_zero(chan->macrocontext)) 
679                         strncpy(exitcontext, chan->macrocontext, sizeof(exitcontext) - 1);
680                 else
681                         strncpy(exitcontext, chan->context, sizeof(exitcontext) - 1);
682         }
683
684         if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
685                 snprintf(user->namerecloc,sizeof(user->namerecloc),"%s/meetme/meetme-username-%s-%d",ast_config_AST_SPOOL_DIR,conf->confno,user->user_no);
686                 ast_record_review(chan,"vm-rec-name",user->namerecloc, 10,"sln", &duration, NULL);
687         }
688
689         conf->users++;
690
691         if (!(confflags & CONFFLAG_QUIET)) {
692                 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
693                         if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
694                                 ast_waitstream(chan, "");
695                 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
696                         if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
697                                 ast_waitstream(chan, "");
698         }
699
700         if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
701                 int keepplaying=1;
702
703                 if (conf->users == 2) { 
704                         if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
705                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
706                                 if (res > 0)
707                                         keepplaying=0;
708                                 else if (res == -1)
709                                         goto outrun;
710                         }
711                 } else { 
712                         if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
713                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
714                                 if (res > 0)
715                                         keepplaying=0;
716                                 else if (res == -1)
717                                         goto outrun;
718                         }
719                         if (keepplaying) {
720                                 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
721                                 if (res > 0)
722                                         keepplaying=0;
723                                 else if (res == -1)
724                                         goto outrun;
725                         }
726                         if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
727                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
728                                 if (res > 0)
729                                         keepplaying=0;
730                                 else if (res == -1) 
731                                         goto outrun;
732                         }
733                 }
734         }
735
736         /* Set it into linear mode (write) */
737         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
738                 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
739                 goto outrun;
740         }
741
742         /* Set it into linear mode (read) */
743         if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
744                 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
745                 goto outrun;
746         }
747         ast_indicate(chan, -1);
748         retryzap = strcasecmp(chan->type, "Zap");
749 zapretry:
750         origfd = chan->fds[0];
751         if (retryzap) {
752                 fd = open("/dev/zap/pseudo", O_RDWR);
753                 if (fd < 0) {
754                         ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
755                         goto outrun;
756                 }
757                 using_pseudo = 1;
758                 /* Make non-blocking */
759                 flags = fcntl(fd, F_GETFL);
760                 if (flags < 0) {
761                         ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
762                         close(fd);
763                         goto outrun;
764                 }
765                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
766                         ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
767                         close(fd);
768                         goto outrun;
769                 }
770                 /* Setup buffering information */
771                 memset(&bi, 0, sizeof(bi));
772                 bi.bufsize = CONF_SIZE/2;
773                 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
774                 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
775                 bi.numbufs = 4;
776                 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
777                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
778                         close(fd);
779                         goto outrun;
780                 }
781                 x = 1;
782                 if (ioctl(fd, ZT_SETLINEAR, &x)) {
783                         ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
784                         close(fd);
785                         goto outrun;
786                 }
787                 nfds = 1;
788         } else {
789                 /* XXX Make sure we're not running on a pseudo channel XXX */
790                 fd = chan->fds[0];
791                 nfds = 0;
792         }
793         memset(&ztc, 0, sizeof(ztc));
794         memset(&ztc_empty, 0, sizeof(ztc_empty));
795         /* Check to see if we're in a conference... */
796         ztc.chan = 0;   
797         if (ioctl(fd, ZT_GETCONF, &ztc)) {
798                 ast_log(LOG_WARNING, "Error getting conference\n");
799                 close(fd);
800                 goto outrun;
801         }
802         if (ztc.confmode) {
803                 /* Whoa, already in a conference...  Retry... */
804                 if (!retryzap) {
805                         ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
806                         retryzap = 1;
807                         goto zapretry;
808                 }
809         }
810         memset(&ztc, 0, sizeof(ztc));
811         /* Add us to the conference */
812         ztc.chan = 0;   
813         ztc.confno = conf->zapconf;
814         ast_mutex_lock(&conflock);
815         if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER) && conf->users > 1) {
816                 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
817                         if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
818                                 ast_waitstream(conf->chan, "");
819                         if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
820                                 ast_waitstream(conf->chan, "");
821                 }
822         }
823
824         if (confflags & CONFFLAG_MONITOR)
825                 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
826         else if (confflags & CONFFLAG_TALKER)
827                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
828         else 
829                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
830
831         if (ioctl(fd, ZT_SETCONF, &ztc)) {
832                 ast_log(LOG_WARNING, "Error setting conference\n");
833                 close(fd);
834                 ast_mutex_unlock(&conflock);
835                 goto outrun;
836         }
837         ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
838
839         manager_event(EVENT_FLAG_CALL, "MeetmeJoin", 
840                         "Channel: %s\r\n"
841                         "Uniqueid: %s\r\n"
842                         "Meetme: %s\r\n"
843                         "Usernum: %d\r\n",
844                         chan->name, chan->uniqueid, conf->confno, user->user_no);
845
846         if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
847                 firstpass = 1;
848                 if (!(confflags & CONFFLAG_QUIET))
849                         if (!(confflags & CONFFLAG_WAITMARKED) || (conf->markedusers >= 1))
850                                 conf_play(chan, conf, ENTER);
851         }
852         conf_flush(fd);
853         ast_mutex_unlock(&conflock);
854         if (confflags & CONFFLAG_AGI) {
855
856                 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
857                   or use default filename of conf-background.agi */
858
859                 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
860                 if (!agifile)
861                         agifile = agifiledefault;
862
863                 if (!strcasecmp(chan->type,"Zap")) {
864                         /*  Set CONFMUTE mode on Zap channel to mute DTMF tones */
865                         x = 1;
866                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
867                 }
868                 /* Find a pointer to the agi app and execute the script */
869                 app = pbx_findapp("agi");
870                 if (app) {
871                         ret = pbx_exec(chan, app, agifile, 1);
872                 } else {
873                         ast_log(LOG_WARNING, "Could not find application (agi)\n");
874                         ret = -2;
875                 }
876                 if (!strcasecmp(chan->type,"Zap")) {
877                         /*  Remove CONFMUTE mode on Zap channel */
878                         x = 0;
879                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
880                 }
881         } else {
882                 if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
883                         /*  Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
884                         x = 1;
885                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
886                 }       
887                 if (confflags &  CONFFLAG_MONITORTALKER && !(dsp = ast_dsp_new())) {
888                         ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
889                         res = -1;
890                 }
891                 for(;;) {
892                         outfd = -1;
893                         ms = -1;
894                         currentmarked = conf->markedusers;
895                         if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_MARKEDUSER) && (confflags & CONFFLAG_WAITMARKED) && lastmarked == 0) {
896                                 if (currentmarked == 1 && conf->users > 1) {
897                                         ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
898                                         if (conf->users - 1 == 1) {
899                                                 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
900                                                         ast_waitstream(chan, "");
901                                         } else {
902                                                 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
903                                                         ast_waitstream(chan, "");
904                                         }
905                                 }
906                                 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
907                                         if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
908                                                 ast_waitstream(chan, "");
909                         }
910
911                         c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
912                         
913                         /* Update the struct with the actual confflags */
914                         user->userflags = confflags;
915                         
916                         if (confflags & CONFFLAG_WAITMARKED) {
917                                 if(currentmarked == 0) {
918                                         if (lastmarked != 0) {
919                                                 if (!(confflags & CONFFLAG_QUIET))
920                                                         if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
921                                                                 ast_waitstream(chan, "");
922                                                 if(confflags & CONFFLAG_MARKEDEXIT)
923                                                         break;
924                                                 else {
925                                                         ztc.confmode = ZT_CONF_CONF;
926                                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
927                                                                 ast_log(LOG_WARNING, "Error setting conference\n");
928                                                                 close(fd);
929                                                                 goto outrun;
930                                                         }
931                                                 }
932                                         }
933                                         if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
934                                                 ast_moh_start(chan, NULL);
935                                                 musiconhold = 1;
936                                         } else {
937                                                 ztc.confmode = ZT_CONF_CONF;
938                                                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
939                                                         ast_log(LOG_WARNING, "Error setting conference\n");
940                                                         close(fd);
941                                                         goto outrun;
942                                                 }
943                                         }
944                                 } else if(currentmarked >= 1 && lastmarked == 0) {
945                                         if (confflags & CONFFLAG_MONITOR)
946                                                 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
947                                         else if (confflags & CONFFLAG_TALKER)
948                                                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
949                                         else
950                                                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
951                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
952                                                 ast_log(LOG_WARNING, "Error setting conference\n");
953                                                 close(fd);
954                                                 goto outrun;
955                                         }
956                                         if (musiconhold && (confflags & CONFFLAG_MOH)) {
957                                                 ast_moh_stop(chan);
958                                                 musiconhold = 0;
959                                         }
960                                         if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
961                                                 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
962                                                         ast_waitstream(chan, "");
963                                                 conf_play(chan, conf, ENTER);
964                                         }
965                                 }
966                         }
967
968                         /* trying to add moh for single person conf */
969                         if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
970                                 if (conf->users == 1) {
971                                         if (musiconhold == 0) {
972                                                 ast_moh_start(chan, NULL);
973                                                 musiconhold = 1;
974                                         } 
975                                 } else {
976                                         if (musiconhold) {
977                                                 ast_moh_stop(chan);
978                                                 musiconhold = 0;
979                                         }
980                                 }
981                         }
982                         
983                         /* Leave if the last marked user left */
984                         if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
985                                 ret = -1;
986                                 break;
987                         }
988         
989                         /* Check if the admin changed my modes */
990                         if (user->adminflags) {                 
991                                 /* Set the new modes */
992                                 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
993                                         ztc.confmode ^= ZT_CONF_TALKER;
994                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
995                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
996                                                 ret = -1;
997                                                 break;
998                                         }
999                                 }
1000                                 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1001                                         ztc.confmode |= ZT_CONF_TALKER;
1002                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1003                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1004                                                 ret = -1;
1005                                                 break;
1006                                         }
1007                                 }
1008                                 if (user->adminflags & ADMINFLAG_KICKME) {
1009                                         /* You have been kicked. */
1010                                         if (!ast_streamfile(chan, "conf-kicked", chan->language))
1011                                                 ast_waitstream(chan, "");
1012                                         ret = 0;
1013                                         break;
1014                                 }
1015                         } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1016                                 ztc.confmode |= ZT_CONF_TALKER;
1017                                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1018                                         ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1019                                         ret = -1;
1020                                         break;
1021                                 }
1022                         }
1023
1024                         if (c) {
1025                                 if (c->fds[0] != origfd) {
1026                                         if (using_pseudo) {
1027                                                 /* Kill old pseudo */
1028                                                 close(fd);
1029                                         }
1030                                         ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
1031                                         retryzap = 0;
1032                                         using_pseudo = 0;
1033                                         goto zapretry;
1034                                 }
1035                                 f = ast_read(c);
1036                                 if (!f)
1037                                         break;
1038                                 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1039                                         if (confflags &  CONFFLAG_MONITORTALKER) {
1040                                                 int totalsilence;
1041                                                 if (user->talking == -1)
1042                                                         user->talking = 0;
1043
1044                                                 res = ast_dsp_silence(dsp, f, &totalsilence);
1045                                                 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1046                                                         user->talking = 1;
1047                                                         manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1048                                                                 "Channel: %s\r\n"
1049                                                                 "Uniqueid: %s\r\n"
1050                                                                 "Meetme: %s\r\n"
1051                                                                 "Usernum: %d\r\n",
1052                                                                 chan->name, chan->uniqueid, conf->confno, user->user_no);
1053                                                 }
1054                                                 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1055                                                         user->talking = 0;
1056                                                         manager_event(EVENT_FLAG_CALL, "MeetmeStopTalking",
1057                                                                 "Channel: %s\r\n"
1058                                                                 "Uniqueid: %s\r\n"
1059                                                                 "Meetme: %s\r\n"
1060                                                                 "Usernum: %d\r\n",
1061                                                                 chan->name, chan->uniqueid, conf->confno, user->user_no);
1062                                                 }
1063                                         }
1064                                         if (using_pseudo) {
1065                                                 /* Carefully write */
1066                                                 careful_write(fd, f->data, f->datalen);
1067                                         }
1068                                 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
1069                                         char tmp[2];
1070                                         tmp[0] = f->subclass;
1071                                         tmp[1] = '\0';
1072                                         if (ast_exists_extension(chan, exitcontext, tmp, 1, chan->cid.cid_num)) {
1073                                                 strncpy(chan->context, exitcontext, sizeof(chan->context) - 1);
1074                                                 strncpy(chan->exten, tmp, sizeof(chan->exten) - 1);
1075                                                 chan->priority = 0;
1076                                                 ret = 0;
1077                                                 break;
1078                                         }
1079                                 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
1080                                         ret = 0;
1081                                         break;
1082                                 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
1083                                         if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
1084                                                 ast_log(LOG_WARNING, "Error setting conference\n");
1085                                                 close(fd);
1086                                                 ast_mutex_unlock(&conflock);
1087                                                 goto outrun;
1088                                         }
1089                                         if (musiconhold) {
1090                                                 ast_moh_stop(chan);
1091                                         }
1092                                         if ((confflags & CONFFLAG_ADMIN)) {
1093                                                 /* Admin menu */
1094                                                 if (!menu_active) {
1095                                                         menu_active = 1;
1096                                                         /* Record this sound! */
1097                                                          if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
1098                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1099                                                         else 
1100                                                                 dtmf = 0;
1101                                                 } else 
1102                                                         dtmf = f->subclass;
1103                                                 if (dtmf) {
1104                                                         switch(dtmf) {
1105                                                                 case '1': /* Un/Mute */
1106                                                                         menu_active = 0;
1107                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
1108                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1109                                                                         confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1110                                                                         } else {
1111                                                                                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1112                                                                                 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1113                                                                         }
1114                                                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1115                                                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1116                                                                                 ret = -1;
1117                                                                                 break;
1118                                                                         }
1119                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
1120                                                                                 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1121                                                                                         ast_waitstream(chan, "");
1122                                                                         } else {
1123                                                                                 if (!ast_streamfile(chan, "conf-muted", chan->language))
1124                                                                                         ast_waitstream(chan, "");
1125                                                                         }
1126                                                                         break;
1127                                                                 case '2': /* Un/Lock the Conference */
1128                                                                         menu_active = 0;
1129                                                                         if (conf->locked) {
1130                                                                                 conf->locked = 0;
1131                                                                                 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1132                                                                                         ast_waitstream(chan, "");
1133                                                                         } else {
1134                                                                                 conf->locked = 1;
1135                                                                                 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1136                                                                                         ast_waitstream(chan, "");
1137                                                                         }
1138                                                                         break;
1139                                                                 case '3': /* Eject last user */
1140                                                                         menu_active = 0;
1141                                                                         usr = conf->lastuser;
1142                                                                         if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1143                                                                                 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1144                                                                                         ast_waitstream(chan, "");
1145                                                                         } else 
1146                                                                                 usr->adminflags |= ADMINFLAG_KICKME;
1147                                                                         ast_stopstream(chan);
1148                                                                         break;  
1149                                                                 default:
1150                                                                         menu_active = 0;
1151                                                                         /* Play an error message! */
1152                                                                         if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1153                                                                                 ast_waitstream(chan, "");
1154                                                                         break;
1155                                                         }
1156                                                 }
1157                                         } else {
1158                                                 /* User menu */
1159                                                 if (!menu_active) {
1160                                                         menu_active = 1;
1161                                                         /* Record this sound! */
1162                                                         if (!ast_streamfile(chan, "conf-usermenu", chan->language))
1163                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1164                                                         else
1165                                                                 dtmf = 0;
1166                                                 } else 
1167                                                         dtmf = f->subclass;
1168                                                 if (dtmf) {
1169                                                         switch(dtmf) {
1170                                                                 case '1': /* Un/Mute */
1171                                                                         menu_active = 0;
1172                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
1173                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1174                                                                         confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1175                                                                         } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
1176                                                                                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1177                                                                                 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1178                                                                         }
1179                                                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1180                                                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1181                                                                                 ret = -1;
1182                                                                                 break;
1183                                                                         }
1184                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
1185                                                                                 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1186                                                                                         ast_waitstream(chan, "");
1187                                                                         } else {
1188                                                                                 if (!ast_streamfile(chan, "conf-muted", chan->language))
1189                                                                                         ast_waitstream(chan, "");
1190                                                                         }
1191                                                                         break;
1192                                                                 default:
1193                                                                         menu_active = 0;
1194                                                                         /* Play an error message! */
1195                                                                         if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1196                                                                                 ast_waitstream(chan, "");
1197                                                                         break;
1198                                                         }
1199                                                 }
1200                                         }
1201                                         if (musiconhold) {
1202                                                 ast_moh_start(chan, NULL);
1203                                         }
1204
1205                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1206                                                 ast_log(LOG_WARNING, "Error setting conference\n");
1207                                                 close(fd);
1208                                                 ast_mutex_unlock(&conflock);
1209                                                 goto outrun;
1210                                         }
1211                                         conf_flush(fd);
1212                                 } else if (option_debug) {
1213                                         ast_log(LOG_DEBUG, "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",chan->name,f->frametype,f->subclass);
1214                                 }
1215                                 ast_frfree(f);
1216                         } else if (outfd > -1) {
1217                                 res = read(outfd, buf, CONF_SIZE);
1218                                 if (res > 0) {
1219                                         memset(&fr, 0, sizeof(fr));
1220                                         fr.frametype = AST_FRAME_VOICE;
1221                                         fr.subclass = AST_FORMAT_SLINEAR;
1222                                         fr.datalen = res;
1223                                         fr.samples = res/2;
1224                                         fr.data = buf;
1225                                         fr.offset = AST_FRIENDLY_OFFSET;
1226                                         if (ast_write(chan, &fr) < 0) {
1227                                                 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1228                                                 /* break; */
1229                                         }
1230                                 } else 
1231                                         ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1232                         }
1233                         lastmarked = currentmarked;
1234                 }
1235         }
1236         if (using_pseudo)
1237                 close(fd);
1238         else {
1239                 /* Take out of conference */
1240                 /* Add us to the conference */
1241                 ztc.chan = 0;   
1242                 ztc.confno = 0;
1243                 ztc.confmode = 0;
1244                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1245                         ast_log(LOG_WARNING, "Error setting conference\n");
1246                 }
1247         }
1248
1249         ast_mutex_lock(&conflock);
1250         if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1251                 conf_play(chan, conf, LEAVE);
1252
1253         if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
1254                 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
1255                         if ((conf->chan) && (conf->users > 1)) {
1256                                 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1257                                         ast_waitstream(conf->chan, "");
1258                                 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1259                                         ast_waitstream(conf->chan, "");
1260                         }
1261                         ast_filedelete(user->namerecloc, NULL);
1262                 }
1263         }
1264         ast_mutex_unlock(&conflock);
1265
1266
1267 outrun:
1268         ast_mutex_lock(&conflock);
1269         if (confflags &  CONFFLAG_MONITORTALKER && dsp)
1270                 ast_dsp_free(dsp);
1271         
1272         if (user->user_no) { /* Only cleanup users who really joined! */
1273                 manager_event(EVENT_FLAG_CALL, "MeetmeLeave", 
1274                         "Channel: %s\r\n"
1275                         "Uniqueid: %s\r\n"
1276                         "Meetme: %s\r\n"
1277                         "Usernum: %d\r\n",
1278                         chan->name, chan->uniqueid, conf->confno, user->user_no);
1279                 prev = NULL;
1280                 conf->users--;
1281                 if (confflags & CONFFLAG_MARKEDUSER) 
1282                         conf->markedusers--;
1283                 cur = confs;
1284                 if (!conf->users) {
1285                         /* No more users -- close this one out */
1286                         while(cur) {
1287                                 if (cur == conf) {
1288                                         if (prev)
1289                                                 prev->next = conf->next;
1290                                         else
1291                                                 confs = conf->next;
1292                                         break;
1293                                 }
1294                                 prev = cur;
1295                                 cur = cur->next;
1296                         }
1297                         if (!cur) 
1298                                 ast_log(LOG_WARNING, "Conference not found\n");
1299                         if (conf->recording == MEETME_RECORD_ACTIVE) {
1300                                 conf->recording = MEETME_RECORD_TERMINATE;
1301                                 ast_mutex_unlock(&conflock);
1302                                 while (1) {
1303                                         ast_mutex_lock(&conflock);
1304                                         if (conf->recording == MEETME_RECORD_OFF)
1305                                                 break;
1306                                         ast_mutex_unlock(&conflock);
1307                                 }
1308                         }
1309                         if (conf->chan)
1310                                 ast_hangup(conf->chan);
1311                         else
1312                                 close(conf->fd);
1313                         free(conf);
1314                 } else {
1315                         /* Remove the user struct */ 
1316                         if (user == conf->firstuser) {
1317                                 if (user->nextuser) {
1318                                         /* There is another entry */
1319                                         user->nextuser->prevuser = NULL;
1320                                 } else {
1321                                         /* We are the only entry */
1322                                         conf->lastuser = NULL;
1323                                 }
1324                                 /* In either case */
1325                                 conf->firstuser = user->nextuser;
1326                         } else if (user == conf->lastuser){
1327                                 if (user->prevuser)
1328                                         user->prevuser->nextuser = NULL;
1329                                 else
1330                                         ast_log(LOG_ERROR, "Bad bad bad!  We're the last, not the first, but nobody before us??\n");
1331                                 conf->lastuser = user->prevuser;
1332                         } else {
1333                                 if (user->nextuser)
1334                                         user->nextuser->prevuser = user->prevuser;
1335                                 else
1336                                         ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
1337                                 if (user->prevuser)
1338                                         user->prevuser->nextuser = user->nextuser;
1339                                 else
1340                                         ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
1341                         }
1342                 }
1343                 /* Return the number of seconds the user was in the conf */
1344                 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
1345                 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1346         }
1347         free(user);
1348         ast_mutex_unlock(&conflock);
1349         return ret;
1350 }
1351
1352 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
1353 {
1354         struct ast_config *cfg;
1355         struct ast_variable *var;
1356         struct ast_conference *cnf;
1357
1358         /* Check first in the conference list */
1359         ast_mutex_lock(&conflock);
1360         cnf = confs;
1361         while (cnf) {
1362                 if (!strcmp(confno, cnf->confno)) 
1363                         break;
1364                 cnf = cnf->next;
1365         }
1366         ast_mutex_unlock(&conflock);
1367
1368         if (!cnf) {
1369                 if (dynamic) {
1370                         /* No need to parse meetme.conf */
1371                         ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1372                         if (dynamic_pin) {
1373                                 if (dynamic_pin[0] == 'q') {
1374                                         /* Query the user to enter a PIN */
1375                                         ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
1376                                 }
1377                                 cnf = build_conf(confno, dynamic_pin, "", make, dynamic);
1378                         } else {
1379                                 cnf = build_conf(confno, "", "", make, dynamic);
1380                         }
1381                 } else {
1382                         /* Check the config */
1383                         cfg = ast_config_load("meetme.conf");
1384                         if (!cfg) {
1385                                 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
1386                                 return NULL;
1387                         }
1388                         var = ast_variable_browse(cfg, "rooms");
1389                         while(var) {
1390                                 if (!strcasecmp(var->name, "conf")) {
1391                                         /* Separate the PIN */
1392                                         char *pin, *pinadmin, *conf;
1393
1394                                         if ((pinadmin = ast_strdupa(var->value))) {
1395                                                 conf = strsep(&pinadmin, "|,");
1396                                                 pin = strsep(&pinadmin, "|,");
1397                                                 if (!strcasecmp(conf, confno)) {
1398                                                         /* Bingo it's a valid conference */
1399                                                         if (pin)
1400                                                                 if (pinadmin)
1401                                                                         cnf = build_conf(confno, pin, pinadmin, make, dynamic);
1402                                                                 else
1403                                                                         cnf = build_conf(confno, pin, "", make, dynamic);
1404                                                         else
1405                                                                 if (pinadmin)
1406                                                                         cnf = build_conf(confno, "", pinadmin, make, dynamic);
1407                                                                 else
1408                                                                         cnf = build_conf(confno, "", "", make, dynamic);
1409                                                         break;
1410                                                 }
1411                                         }
1412                                 }
1413                                 var = var->next;
1414                         }
1415                         if (!var) {
1416                                 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1417                         }
1418                         ast_config_destroy(cfg);
1419                 }
1420         } else if (dynamic_pin) {
1421                 /* Correct for the user selecting 'D' instead of 'd' to have
1422                    someone join into a conference that has already been created
1423                    with a pin. */
1424                 if (dynamic_pin[0] == 'q')
1425                         dynamic_pin[0] = '\0';
1426         }
1427         return cnf;
1428 }
1429
1430 /*--- count_exec: The MeetmeCount application */
1431 static int count_exec(struct ast_channel *chan, void *data)
1432 {
1433         struct localuser *u;
1434         int res = 0;
1435         struct ast_conference *conf;
1436         int count;
1437         char *confnum, *localdata;
1438         char val[80] = "0"; 
1439
1440         if (!data || ast_strlen_zero(data)) {
1441                 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1442                 return -1;
1443         }
1444         localdata = ast_strdupa(data);
1445         LOCAL_USER_ADD(u);
1446         confnum = strsep(&localdata,"|");       
1447         conf = find_conf(chan, confnum, 0, 0, NULL);
1448         if (conf)
1449                 count = conf->users;
1450         else
1451                 count = 0;
1452
1453         if (localdata && !ast_strlen_zero(localdata)){
1454                 /* have var so load it and exit */
1455                 snprintf(val,sizeof(val), "%d",count);
1456                 pbx_builtin_setvar_helper(chan, localdata,val);
1457         } else {
1458                 if (chan->_state != AST_STATE_UP)
1459                         ast_answer(chan);
1460                 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1461         }
1462         LOCAL_USER_REMOVE(u);
1463         return res;
1464 }
1465
1466 /*--- conf_exec: The meetme() application */
1467 static int conf_exec(struct ast_channel *chan, void *data)
1468 {
1469         int res=-1;
1470         struct localuser *u;
1471         char confno[AST_MAX_EXTENSION] = "";
1472         int allowretry = 0;
1473         int retrycnt = 0;
1474         struct ast_conference *cnf;
1475         struct ast_flags confflags = {0};
1476         int dynamic = 0;
1477         int empty = 0, empty_no_pin = 0;
1478         int always_prompt = 0;
1479         char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
1480
1481         if (!data || ast_strlen_zero(data)) {
1482                 allowretry = 1;
1483                 notdata = "";
1484         } else {
1485                 notdata = data;
1486         }
1487         LOCAL_USER_ADD(u);
1488         if (chan->_state != AST_STATE_UP)
1489                 ast_answer(chan);
1490
1491         info = ast_strdupa((char *)notdata);
1492
1493         if (info) {
1494                 char *tmp = strsep(&info, "|");
1495                 strncpy(confno, tmp, sizeof(confno) - 1);
1496                 if (ast_strlen_zero(confno)) {
1497                         allowretry = 1;
1498                 }
1499         }
1500         if (info)
1501                 inflags = strsep(&info, "|");
1502         if (info)
1503                 inpin = strsep(&info, "|");
1504         if (inpin)
1505                 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
1506
1507         if (inflags) {
1508                 ast_parseoptions(meetme_opts, &confflags, NULL, inflags);
1509                 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
1510                 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !inpin)
1511                         strncpy(the_pin, "q", sizeof(the_pin) - 1);
1512
1513                 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
1514                 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
1515                 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
1516         }
1517
1518         do {
1519                 if (retrycnt > 3)
1520                         allowretry = 0;
1521                 if (empty) {
1522                         int i, map[1024];
1523                         struct ast_config *cfg;
1524                         struct ast_variable *var;
1525                         int confno_int;
1526
1527                         memset(map, 0, sizeof(map));
1528
1529                         ast_mutex_lock(&conflock);
1530                         cnf = confs;
1531                         while (cnf) {
1532                                 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
1533                                         /* Disqualify in use conference */
1534                                         if (confno_int >= 0 && confno_int < 1024)
1535                                                 map[confno_int]++;
1536                                 }
1537                                 cnf = cnf->next;
1538                         }
1539                         ast_mutex_unlock(&conflock);
1540
1541                         /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
1542                         if ((empty_no_pin) || (!dynamic)) {
1543                                 cfg = ast_config_load("meetme.conf");
1544                                 if (cfg) {
1545                                         var = ast_variable_browse(cfg, "rooms");
1546                                         while(var) {
1547                                                 if (!strcasecmp(var->name, "conf")) {
1548                                                         char *stringp = ast_strdupa(var->value);
1549                                                         if (stringp) {
1550                                                                 char *confno_tmp = strsep(&stringp, "|,");
1551                                                                 int found = 0;
1552                                                                 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
1553                                                                         if ((confno_int >= 0) && (confno_int < 1024)) {
1554                                                                                 if (stringp && empty_no_pin) {
1555                                                                                         map[confno_int]++;
1556                                                                                 }
1557                                                                         }
1558                                                                 }
1559                                                                 if (! dynamic) {
1560                                                                         /* For static:  run through the list and see if this conference is empty */
1561                                                                         ast_mutex_lock(&conflock);
1562                                                                         cnf = confs;
1563                                                                         while (cnf) {
1564                                                                                 if (!strcmp(confno_tmp, cnf->confno)) {
1565                                                                                         /* The conference exists, therefore it's not empty */
1566                                                                                         found = 1;
1567                                                                                         break;
1568                                                                                 }
1569                                                                                 cnf = cnf->next;
1570                                                                         }
1571                                                                         ast_mutex_unlock(&conflock);
1572                                                                         if (!found) {
1573                                                                                 /* At this point, we have a confno_tmp (static conference) that is empty */
1574                                                                                 if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) {
1575                                                                                 /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
1576                                                                                  * Case 2:  empty_no_pin and pin is blank (but not NULL)
1577                                                                                  * Case 3:  not empty_no_pin
1578                                                                                  */
1579                                                                                         strncpy(confno, confno_tmp, sizeof(confno) - 1);
1580                                                                                         break;
1581                                                                                         /* XXX the map is not complete (but we do have a confno) */
1582                                                                                 }
1583                                                                         }
1584                                                                 }
1585                                                         } else {
1586                                                                 ast_log(LOG_ERROR, "Out of memory\n");
1587                                                         }
1588                                                 }
1589                                                 var = var->next;
1590                                         }
1591                                         ast_config_destroy(cfg);
1592                                 }
1593                         }
1594                         /* Select first conference number not in use */
1595                         if (ast_strlen_zero(confno) && dynamic) {
1596                                 for (i=0;i<1024;i++) {
1597                                         if (!map[i]) {
1598                                                 snprintf(confno, sizeof(confno), "%d", i);
1599                                                 break;
1600                                         }
1601                                 }
1602                         }
1603
1604                         /* Not found? */
1605                         if (ast_strlen_zero(confno)) {
1606                                 res = ast_streamfile(chan, "conf-noempty", chan->language);
1607                                 if (!res)
1608                                         ast_waitstream(chan, "");
1609                         } else {
1610                                 if (sscanf(confno, "%d", &confno_int) == 1) {
1611                                         res = ast_streamfile(chan, "conf-enteringno", chan->language);
1612                                         if (!res) {
1613                                                 ast_waitstream(chan, "");
1614                                                 res = ast_say_digits(chan, confno_int, "", chan->language);
1615                                         }
1616                                 } else {
1617                                         ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
1618                                 }
1619                         }
1620                 }
1621                 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
1622                         /* Prompt user for conference number */
1623                         res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
1624                         if (res < 0) {
1625                                 /* Don't try to validate when we catch an error */
1626                                 confno[0] = '\0';
1627                                 allowretry = 0;
1628                                 break;
1629                         }
1630                 }
1631                 if (!ast_strlen_zero(confno)) {
1632                         /* Check the validity of the conference */
1633                         cnf = find_conf(chan, confno, 1, dynamic, the_pin);
1634                         if (!cnf) {
1635                                 res = ast_streamfile(chan, "conf-invalid", chan->language);
1636                                 if (!res)
1637                                         ast_waitstream(chan, "");
1638                                 res = -1;
1639                                 if (allowretry)
1640                                         confno[0] = '\0';
1641                         } else {
1642                                 if ((!ast_strlen_zero(cnf->pin) &&  !ast_test_flag(&confflags, CONFFLAG_ADMIN)) || (!ast_strlen_zero(cnf->pinadmin) && ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
1643                                         char pin[AST_MAX_EXTENSION]="";
1644                                         int j;
1645
1646                                         /* Allow the pin to be retried up to 3 times */
1647                                         for (j=0; j<3; j++) {
1648                                                 if (*the_pin && (always_prompt==0)) {
1649                                                         strncpy(pin, the_pin, sizeof(pin) - 1);
1650                                                         res = 0;
1651                                                 } else {
1652                                                         /* Prompt user for pin if pin is required */
1653                                                         res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
1654                                                 }
1655                                                 if (res >= 0) {
1656                                                         if (!strcasecmp(pin, cnf->pin)  || (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))) {
1657
1658                                                                 /* Pin correct */
1659                                                                 allowretry = 0;
1660                                                                 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) 
1661                                                                         ast_set_flag(&confflags, CONFFLAG_ADMIN);
1662                                                                 /* Run the conference */
1663                                                                 res = conf_run(chan, cnf, confflags.flags);
1664                                                                 break;
1665                                                         } else {
1666                                                                 /* Pin invalid */
1667                                                                 res = ast_streamfile(chan, "conf-invalidpin", chan->language);
1668                                                                 if (!res)
1669                                                                         ast_waitstream(chan, AST_DIGIT_ANY);
1670                                                                 if (res < 0)
1671                                                                         break;
1672                                                                 pin[0] = res;
1673                                                                 pin[1] = '\0';
1674                                                                 res = -1;
1675                                                                 if (allowretry)
1676                                                                         confno[0] = '\0';
1677                                                         }
1678                                                 } else {
1679                                                         res = -1;
1680                                                         allowretry = 0;
1681                                                         break;
1682                                                 }
1683
1684                                                 /* Don't retry pin with a static pin */
1685                                                 if (*the_pin && (always_prompt==0)) {
1686                                                         break;
1687                                                 }
1688                                         }
1689                                 } else {
1690                                         /* No pin required */
1691                                         allowretry = 0;
1692
1693                                         /* Run the conference */
1694                                         res = conf_run(chan, cnf, confflags.flags);
1695                                 }
1696                         }
1697                 }
1698         } while (allowretry);
1699         /* Do the conference */
1700         LOCAL_USER_REMOVE(u);
1701         return res;
1702 }
1703
1704 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
1705         struct ast_conf_user *user = NULL;
1706         char usrno[1024] = "";
1707         if (conf && callerident) {
1708                 user = conf->firstuser;
1709                 while(user) {
1710                         snprintf(usrno, sizeof(usrno), "%d", user->user_no);
1711                         if (strcmp(usrno, callerident) == 0)
1712                                 return user;
1713                         user = user->nextuser;
1714                 }
1715         }
1716         return NULL;
1717 }
1718
1719 /*--- admin_exec: The MeetMeadmin application */
1720 /* MeetMeAdmin(confno, command, caller) */
1721 static int admin_exec(struct ast_channel *chan, void *data) {
1722         char *params, *command = NULL, *caller = NULL, *conf = NULL;
1723         struct ast_conference *cnf;
1724         struct ast_conf_user *user = NULL;
1725
1726         ast_mutex_lock(&conflock);
1727         /* The param has the conference number the user and the command to execute */
1728         if (data && !ast_strlen_zero(data)) {           
1729                 params = ast_strdupa((char *) data);
1730                 conf = strsep(&params, "|");
1731                 command = strsep(&params, "|");
1732                 caller = strsep(&params, "|");
1733                 
1734                 if (!command) {
1735                         ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
1736                         ast_mutex_unlock(&conflock);
1737                         return -1;
1738                 }
1739                 cnf = confs;
1740                 while (cnf) {
1741                         if (strcmp(cnf->confno, conf) == 0) 
1742                                 break;
1743                         cnf = cnf->next;
1744                 }
1745                 
1746                 if (caller)
1747                         user = find_user(cnf, caller);
1748                 
1749                 if (cnf) {
1750                         switch((int) (*command)) {
1751                                 case 76: /* L: Lock */ 
1752                                         cnf->locked = 1;
1753                                         break;
1754                                 case 108: /* l: Unlock */ 
1755                                         cnf->locked = 0;
1756                                         break;
1757                                 case 75: /* K: kick all users*/
1758                                         user = cnf->firstuser;
1759                                         while(user) {
1760                                                 user->adminflags |= ADMINFLAG_KICKME;
1761                                                 if (user->nextuser) {
1762                                                         user = user->nextuser;
1763                                                 } else {
1764                                                         break;
1765                                                 }
1766                                         }
1767                                         break;
1768                                 case 101: /* e: Eject last user*/
1769                                         user = cnf->lastuser;
1770                                         if (!(user->userflags & CONFFLAG_ADMIN)) {
1771                                                 user->adminflags |= ADMINFLAG_KICKME;
1772                                                 break;
1773                                         } else
1774                                                 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
1775                                         break;
1776                                 case 77: /* M: Mute */ 
1777                                         if (user) {
1778                                                 user->adminflags |= ADMINFLAG_MUTED;
1779                                         } else {
1780                                                 ast_log(LOG_NOTICE, "Specified User not found!\n");
1781                                         }
1782                                         break;
1783                                 case 78: /* N: Mute all users */
1784                                         user = cnf->firstuser;
1785                                         while(user) {
1786                                                 if (user && !(user->userflags & CONFFLAG_ADMIN))
1787                                                         user->adminflags |= ADMINFLAG_MUTED;
1788                                                 if (user->nextuser) {
1789                                                         user = user->nextuser;
1790                                                 } else {
1791                                                         break;
1792                                                 }
1793                                         }
1794                                         break;                                  
1795                                 case 109: /* m: Unmute */ 
1796                                         if (user && (user->adminflags & ADMINFLAG_MUTED)) {
1797                                                 user->adminflags ^= ADMINFLAG_MUTED;
1798                                         } else {
1799                                                 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
1800                                         }
1801                                         break;
1802                                 case  110: /* n: Unmute all users */
1803                                         user = cnf->firstuser;
1804                                         while(user) {
1805                                                 if (user && (user-> adminflags & ADMINFLAG_MUTED)) {
1806                                                         user->adminflags ^= ADMINFLAG_MUTED;
1807                                                 }
1808                                                 if (user->nextuser) {
1809                                                         user = user->nextuser;
1810                                                 } else {
1811                                                         break;
1812                                                 }
1813                                         }
1814                                         break;
1815                                 case 107: /* k: Kick user */ 
1816                                         if (user) {
1817                                                 user->adminflags |= ADMINFLAG_KICKME;
1818                                         } else {
1819                                                 ast_log(LOG_NOTICE, "Specified User not found!");
1820                                         }
1821                                         break;
1822                         }
1823                 } else {
1824                         ast_log(LOG_NOTICE, "Conference Number not found\n");
1825                 }
1826         }
1827         ast_mutex_unlock(&conflock);
1828         return 0;
1829 }
1830
1831 static void *recordthread(void *args)
1832 {
1833         struct ast_conference *cnf;
1834         struct ast_frame *f=NULL;
1835         int flags;
1836         struct ast_filestream *s;
1837         int res=0;
1838
1839         cnf = (struct ast_conference *)args;
1840         if( !cnf || !cnf->chan ) {
1841                 pthread_exit(0);
1842         }
1843         ast_stopstream(cnf->chan);
1844         flags = O_CREAT|O_TRUNC|O_WRONLY;
1845         s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
1846
1847         if (s) {
1848                 cnf->recording = MEETME_RECORD_ACTIVE;
1849                 while (ast_waitfor(cnf->chan, -1) > -1) {
1850                         f = ast_read(cnf->chan);
1851                         if (!f) {
1852                                 res = -1;
1853                                 break;
1854                         }
1855                         if (f->frametype == AST_FRAME_VOICE) {
1856                                 res = ast_writestream(s, f);
1857                                 if (res) 
1858                                         break;
1859                         }
1860                         ast_frfree(f);
1861                         if (cnf->recording == MEETME_RECORD_TERMINATE) {
1862                                 ast_mutex_lock(&conflock);
1863                                 ast_mutex_unlock(&conflock);
1864                                 break;
1865                         }
1866                 }
1867                 cnf->recording = MEETME_RECORD_OFF;
1868                 ast_closestream(s);
1869         }
1870         pthread_exit(0);
1871 }
1872
1873 int unload_module(void)
1874 {
1875         STANDARD_HANGUP_LOCALUSERS;
1876         ast_cli_unregister(&cli_show_confs);
1877         ast_cli_unregister(&cli_conf);
1878         ast_unregister_application(app3);
1879         ast_unregister_application(app2);
1880         return ast_unregister_application(app);
1881 }
1882
1883 int load_module(void)
1884 {
1885         ast_cli_register(&cli_show_confs);
1886         ast_cli_register(&cli_conf);
1887         ast_register_application(app3, admin_exec, synopsis3, descrip3);
1888         ast_register_application(app2, count_exec, synopsis2, descrip2);
1889         return ast_register_application(app, conf_exec, synopsis, descrip);
1890 }
1891
1892
1893 char *description(void)
1894 {
1895         return tdesc;
1896 }
1897
1898 int usecount(void)
1899 {
1900         int res;
1901         STANDARD_USECOUNT(res);
1902         return res;
1903 }
1904
1905 char *key()
1906 {
1907         return ASTERISK_GPL_KEY;
1908 }
1909