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