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