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