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