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