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