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