Don't count as outgoing until we actually send the new INVITE
[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         int fd;                         /* Announcements fd */
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->fd, 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->fd = open("/dev/zap/pseudo", O_RDWR);
207                         if (cnf->fd < 0) {
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_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
218                         if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
219                                 ast_log(LOG_WARNING, "Error setting conference\n");
220                                 close(cnf->fd);
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
470         struct ast_app *app;
471         char *agifile;
472         char *agifiledefault = "conf-background.agi";
473         char meetmesecs[30];
474
475         ZT_BUFFERINFO bi;
476         char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
477         char *buf = __buf + AST_FRIENDLY_OFFSET;
478         
479         user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */
480         
481         if (conf->locked) {
482                 /* Sorry, but this confernce is locked! */      
483                 if (!ast_streamfile(chan, "conf-locked", chan->language))
484                         ast_waitstream(chan, "");
485                 goto outrun;
486         }
487         
488         conf->users++;
489         if (confflags & CONFFLAG_ADMINEXIT) {
490                 if (conf->markedusers == -1) {
491                         conf->markedusers = 1;
492                 } else {
493                         conf->markedusers++;
494                 }
495         }
496       
497         ast_mutex_lock(&conflock);
498         if (conf->firstuser == NULL) {
499                 /* Fill the first new User struct */
500                 user->user_no = 1;
501                 user->nextuser = NULL;
502                 user->prevuser = NULL;
503                 conf->firstuser = user;
504                 conf->lastuser = user;
505         } else {
506                 /* Fill the new user struct */  
507                 user->user_no = conf->lastuser->user_no + 1; 
508                 user->prevuser = conf->lastuser;
509                 user->nextuser = NULL;
510                 if (conf->lastuser->nextuser != NULL) {
511                         ast_log(LOG_WARNING, "Error in User Management!\n");
512                         goto outrun;
513                 } else {
514                         conf->lastuser->nextuser = user;
515                         conf->lastuser = user;
516                 }
517         }
518         strncpy(user->usrvalue, "test", sizeof(user->usrvalue));
519         user->chan = chan;
520         user->userflags = confflags;
521         user->adminflags = 0;
522         ast_mutex_unlock(&conflock);
523         
524         if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
525                 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
526                         ast_waitstream(chan, "");
527         }
528
529         if (confflags & CONFFLAG_VIDEO) {       
530                 /* Set it into linear mode (write) */
531                 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
532                         ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
533                         goto outrun;
534                 }
535
536                 /* Set it into linear mode (read) */
537                 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
538                         ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
539                         goto outrun;
540                 }
541         } else {
542                 /* Set it into U-law mode (write) */
543                 if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
544                         ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
545                         goto outrun;
546                 }
547
548                 /* Set it into U-law mode (read) */
549                 if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
550                         ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
551                         goto outrun;
552                 }
553         }
554         ast_indicate(chan, -1);
555         retryzap = strcasecmp(chan->type, "Zap");
556 zapretry:
557         origfd = chan->fds[0];
558         if (retryzap) {
559                 fd = open("/dev/zap/pseudo", O_RDWR);
560                 if (fd < 0) {
561                         ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
562                         goto outrun;
563                 }
564                 /* Make non-blocking */
565                 flags = fcntl(fd, F_GETFL);
566                 if (flags < 0) {
567                         ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
568                         close(fd);
569                         goto outrun;
570                 }
571                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
572                         ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
573                         close(fd);
574                         goto outrun;
575                 }
576                 /* Setup buffering information */
577                 memset(&bi, 0, sizeof(bi));
578                 bi.bufsize = CONF_SIZE;
579                 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
580                 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
581                 bi.numbufs = 4;
582                 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
583                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
584                         close(fd);
585                         goto outrun;
586                 }
587                 if (confflags & CONFFLAG_VIDEO) {       
588                         x = 1;
589                         if (ioctl(fd, ZT_SETLINEAR, &x)) {
590                                 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
591                                 close(fd);
592                                 goto outrun;
593                         }
594                 }
595                 nfds = 1;
596         } else {
597                 /* XXX Make sure we're not running on a pseudo channel XXX */
598                 fd = chan->fds[0];
599                 nfds = 0;
600         }
601         memset(&ztc, 0, sizeof(ztc));
602         /* Check to see if we're in a conference... */
603         ztc.chan = 0;   
604         if (ioctl(fd, ZT_GETCONF, &ztc)) {
605                 ast_log(LOG_WARNING, "Error getting conference\n");
606                 close(fd);
607                 goto outrun;
608         }
609         if (ztc.confmode) {
610                 /* Whoa, already in a conference...  Retry... */
611                 if (!retryzap) {
612                         ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
613                         retryzap = 1;
614                         goto zapretry;
615                 }
616         }
617         memset(&ztc, 0, sizeof(ztc));
618         /* Add us to the conference */
619         ztc.chan = 0;   
620         ztc.confno = conf->zapconf;
621         if (confflags & CONFFLAG_MONITOR)
622                 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
623         else if (confflags & CONFFLAG_TALKER)
624                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
625         else 
626                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
627
628         if (ioctl(fd, ZT_SETCONF, &ztc)) {
629                 ast_log(LOG_WARNING, "Error setting conference\n");
630                 close(fd);
631                 goto outrun;
632         }
633         ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
634
635         manager_event(EVENT_FLAG_CALL, "MeetmeJoin", 
636                         "Channel: %s\r\n"
637                         "Uniqueid: %s\r\n"
638                         "Meetme: %s\r\n",
639                         chan->name, chan->uniqueid, conf->confno);
640
641         if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
642                 firstpass = 1;
643                 if (!(confflags & CONFFLAG_QUIET))
644                         conf_play(conf, ENTER);
645         }
646
647         if (confflags & CONFFLAG_AGI) {
648
649                 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
650                   or use default filename of conf-background.agi */
651
652                 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
653                 if (!agifile)
654                         agifile = agifiledefault;
655
656                 if (!strcasecmp(chan->type,"Zap")) {
657                         /*  Set CONFMUTE mode on Zap channel to mute DTMF tones */
658                         x = 1;
659                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
660                 }
661                 /* Find a pointer to the agi app and execute the script */
662                 app = pbx_findapp("agi");
663                 if (app) {
664                         ret = pbx_exec(chan, app, agifile, 1);
665                 } else {
666                         ast_log(LOG_WARNING, "Could not find application (agi)\n");
667                         ret = -2;
668                 }
669                 if (!strcasecmp(chan->type,"Zap")) {
670                         /*  Remove CONFMUTE mode on Zap channel */
671                         x = 0;
672                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
673                 }
674         } else {
675                 if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
676                         /*  Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
677                         x = 1;
678                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
679                 }       
680                 for(;;) {
681                         outfd = -1;
682                         ms = -1;
683                         c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
684                         
685                         /* Update the struct with the actual confflags */
686                         user->userflags = confflags;
687                         
688                         /* trying to add moh for single person conf */
689                         if (confflags & CONFFLAG_MOH) {
690                                 if (conf->users == 1) {
691                                         if (musiconhold == 0) {
692                                                 ast_moh_start(chan, NULL);
693                                                 musiconhold = 1;
694                                         } 
695                                 } else {
696                                         if (musiconhold) {
697                                                 ast_moh_stop(chan);
698                                                 musiconhold = 0;
699                                         }
700                                 }
701                         }
702                         
703                         /* Leave if the last marked user left */
704                         if ((confflags & CONFFLAG_ADMINEXIT) && (conf->markedusers == 0)) {
705                                 ret = 0;
706                                 break;
707                         }
708         
709                         /* Check if the admin changed my modes */
710                         if (user->adminflags) {                 
711                                 /* Set the new modes */
712                                 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
713                                         ztc.confmode ^= ZT_CONF_TALKER;
714                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
715                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
716                                                 ret = -1;
717                                                 break;
718                                         }
719                                 }
720                                 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
721                                         ztc.confmode |= ZT_CONF_TALKER;
722                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
723                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
724                                                 ret = -1;
725                                                 break;
726                                         }
727                                 }
728                                 if (user->adminflags & ADMINFLAG_KICKME) {
729                                         //You have been kicked.
730                                         if (!ast_streamfile(chan, "conf-kicked", chan->language))
731                                                 ast_waitstream(chan, "");
732                                         ret = 0;
733                                         break;
734                                 }
735                         } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
736                                 ztc.confmode |= ZT_CONF_TALKER;
737                                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
738                                         ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
739                                         ret = -1;
740                                         break;
741                                 }
742                         }
743
744                 if (c) {
745                         if (c->fds[0] != origfd) {
746                                 if (retryzap) {
747                                         /* Kill old pseudo */
748                                         close(fd);
749                                 }
750                                 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
751                                 retryzap = 0;
752                                 goto zapretry;
753                         }
754                         f = ast_read(c);
755                         if (!f) 
756                                 break;
757                         if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
758                                 ret = 0;
759                                 break;
760                                 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
761                                                 if (musiconhold) {
762                                                         ast_moh_stop(chan);
763                                                 }
764                                         if ((confflags & CONFFLAG_ADMIN)) {
765                                                         /* Admin menu */
766                                                         if (!menu_active) {
767                                                                 menu_active = 1;
768                                                                 /* Record this sound! */
769                                                                 if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
770                                                                         ast_waitstream(chan, "");
771                                         } else {
772                                                                 switch(f->subclass - 48) {
773                                                                         case 1: /* Un/Mute */
774                                                                                 menu_active = 0;
775                                                                                 if (ztc.confmode & ZT_CONF_TALKER) {
776                                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
777                                                                                         confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
778                                                                                 } else {
779                                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
780                                                                                         confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
781                                                                                 }
782                                                                                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
783                                                                                         ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
784                                                                                         ret = -1;
785                                                                                         break;
786                                                                                 }
787                                                                                 if (ztc.confmode & ZT_CONF_TALKER) {
788                                                                                         if (!ast_streamfile(chan, "conf-unmuted", chan->language))
789                                                                                                 ast_waitstream(chan, "");
790                                                                                 } else {
791                                                                                         if (!ast_streamfile(chan, "conf-muted", chan->language))
792                                                                                                 ast_waitstream(chan, "");
793                                                                                 }
794                                                                                 break;
795                                                                         case 2: /* Un/Lock the Conference */
796                                                                                 menu_active = 0;
797                                                                                 if (conf->locked) {
798                                                                                         conf->locked = 0;
799                                                                                         if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
800                                                                                                 ast_waitstream(chan, "");
801                                                                                 } else {
802                                                                                         conf->locked = 1;
803                                                                                         if (!ast_streamfile(chan, "conf-lockednow", chan->language))
804                                                                                                 ast_waitstream(chan, "");
805                                         }
806
807                                                                                 break;
808                                                                         default:
809                                                                                 menu_active = 0;
810                                                                                 /* Play an error message! */
811                                                                                 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
812                                                                                         ast_waitstream(chan, "");
813                                                                                 break;
814                                                                 }
815                                                         }
816                                                 } else {
817                                                         /* User menu */
818                                                         if (!menu_active) {
819                                                                 menu_active = 1;
820                                                                 /* Record this sound! */
821                                                                 if (!ast_streamfile(chan, "conf-usermenu", chan->language))
822                                                                         ast_waitstream(chan, "");
823                                                         } else {
824                                                                 switch(f->subclass - 48) {
825                                                                         case 1: /* Un/Mute */
826                                                                                 menu_active = 0;
827                                                                                 if (ztc.confmode & ZT_CONF_TALKER) {
828                                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
829                                                                                         confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
830                                                                                 } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
831                                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
832                                                                                         confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
833                                                                                 }
834                                                                                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
835                                                                                         ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
836                                                                                         ret = -1;
837                                                                                         break;
838                                                                                 }
839                                                                                 if (ztc.confmode & ZT_CONF_TALKER) {
840                                                                                         if (!ast_streamfile(chan, "conf-unmuted", chan->language))
841                                                                                                 ast_waitstream(chan, "");
842                                                                                 } else {
843                                                                                         if (!ast_streamfile(chan, "conf-muted", chan->language))
844                                                                                                 ast_waitstream(chan, "");
845                                                                                 }
846                                                                                 break;
847                                                                         default:
848                                                                                 menu_active = 0;
849                                                                                 /* Play an error message! */
850                                                                                 if (!ast_streamfile(chan, "errormenu", chan->language))
851                                                                                         ast_waitstream(chan, "");
852                                                                                 break;
853                                                                 }
854                                                         }
855                                                 }
856                                                 if (musiconhold) {
857                                                         ast_moh_start(chan, NULL);
858                                                 }
859                         } else if (fd != chan->fds[0]) {
860                                 if (f->frametype == AST_FRAME_VOICE) {
861                                         if (f->subclass == AST_FORMAT_ULAW) {
862                                                 /* Carefully write */
863                                                 careful_write(fd, f->data, f->datalen);
864                                         } else
865                                                 ast_log(LOG_WARNING, "Huh?  Got a non-ulaw (%d) frame in the conference\n", f->subclass);
866                                 }
867                         }
868                         ast_frfree(f);
869                 } else if (outfd > -1) {
870                         res = read(outfd, buf, CONF_SIZE);
871                         if (res > 0) {
872                                 memset(&fr, 0, sizeof(fr));
873                                 fr.frametype = AST_FRAME_VOICE;
874                                 fr.subclass = AST_FORMAT_ULAW;
875                                 fr.datalen = res;
876                                 fr.samples = res;
877                                 fr.data = buf;
878                                 fr.offset = AST_FRIENDLY_OFFSET;
879                                 if (ast_write(chan, &fr) < 0) {
880                                         ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
881                                         /* break; */
882                                 }
883                         } else 
884                                 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
885                 }
886         }
887         }
888         if (fd != chan->fds[0])
889                 close(fd);
890         else {
891                 /* Take out of conference */
892                 /* Add us to the conference */
893                 ztc.chan = 0;   
894                 ztc.confno = 0;
895                 ztc.confmode = 0;
896                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
897                         ast_log(LOG_WARNING, "Error setting conference\n");
898                 }
899         }
900         if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
901                 conf_play(conf, LEAVE);
902
903 outrun:
904         if (user->user_no) { /* Only cleanup users who really joined! */
905         manager_event(EVENT_FLAG_CALL, "MeetmeLeave", 
906                         "Channel: %s\r\n"
907                         "Uniqueid: %s\r\n"
908                         "Meetme: %s\r\n",
909                         chan->name, chan->uniqueid, conf->confno);
910                 ast_mutex_lock(&conflock);
911                 conf->users--;
912                 cur = confs;
913         if (!conf->users) {
914                 /* No more users -- close this one out */
915                 while(cur) {
916                         if (cur == conf) {
917                                 if (prev)
918                                         prev->next = conf->next;
919                                 else
920                                         confs = conf->next;
921                                 break;
922                         }
923                         prev = cur;
924                         cur = cur->next;
925                 }
926                 if (!cur) 
927                         ast_log(LOG_WARNING, "Conference not found\n");
928                 close(conf->fd);
929                 free(conf);
930                 } else {
931                         /* Remove the user struct */ 
932                         if (user == cur->firstuser) {
933                                 cur->firstuser->nextuser->prevuser = NULL;
934                                 cur->firstuser = cur->firstuser->nextuser;
935                         } else if (user == cur->lastuser){
936                                 cur->lastuser->prevuser->nextuser = NULL;
937                                 cur->lastuser = cur->lastuser->prevuser;
938                         } else {
939                                 user->nextuser->prevuser = user->prevuser;
940                                 user->prevuser->nextuser = user->nextuser;
941                         }
942                         /* Return the number of seconds the user was in the conf */
943                         sprintf(meetmesecs, "%i", (int) (user->jointime - time(NULL)));
944                         pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
945                 }
946         }
947         free(user);
948         ast_mutex_unlock(&conflock);
949         return ret;
950 }
951
952 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
953 {
954         struct ast_config *cfg;
955         struct ast_variable *var;
956         struct ast_conference *cnf = confs;
957
958         /* Check first in the conference list */
959         ast_mutex_lock(&conflock);
960         while (cnf) {
961                 if (!strcmp(confno, cnf->confno)) 
962                         break;
963                 cnf = cnf->next;
964         }
965         ast_mutex_unlock(&conflock);
966
967         if (!cnf) {
968                 if (dynamic) {
969                         /* No need to parse meetme.conf */
970                         ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
971                         if (dynamic_pin) {
972                                 if (dynamic_pin[0] == 'q') {
973                                         /* Query the user to enter a PIN */
974                                         ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
975                                 }
976                                 cnf = build_conf(confno, dynamic_pin, make, dynamic);
977                         } else {
978                                 cnf = build_conf(confno, "", make, dynamic);
979                         }
980                 } else {
981                         /* Check the config */
982                         cfg = ast_load("meetme.conf");
983                         if (!cfg) {
984                                 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
985                                 return NULL;
986                         }
987                         var = ast_variable_browse(cfg, "rooms");
988                         while(var) {
989                                 if (!strcasecmp(var->name, "conf")) {
990                                         /* Separate the PIN */
991                                         char *pin, *conf;
992
993                                         if ((pin = ast_strdupa(var->value))) {
994                                                 conf = strsep(&pin, "|,");
995                                                 if (!strcasecmp(conf, confno)) {
996                                                         /* Bingo it's a valid conference */
997                                                         if (pin)
998                                                                 cnf = build_conf(confno, pin, make, dynamic);
999                                                         else
1000                                                                 cnf = build_conf(confno, "", make, dynamic);
1001                                                         break;
1002                                                 }
1003                                         }
1004                                 }
1005                                 var = var->next;
1006                         }
1007                         if (!var) {
1008                                 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1009                         }
1010                         ast_destroy(cfg);
1011                 }
1012         }
1013         return cnf;
1014 }
1015
1016 /*--- count_exec: The MeetmeCount application */
1017 static int count_exec(struct ast_channel *chan, void *data)
1018 {
1019         struct localuser *u;
1020         int res = 0;
1021         struct ast_conference *conf;
1022         int count;
1023         char *confnum, *localdata;
1024         char val[80] = "0"; 
1025
1026         if (!data || ast_strlen_zero(data)) {
1027                 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1028                 return -1;
1029         }
1030         localdata = ast_strdupa(data);
1031         LOCAL_USER_ADD(u);
1032         confnum = strsep(&localdata,"|");       
1033         conf = find_conf(chan, confnum, 0, 0, NULL);
1034         if (conf)
1035                 count = conf->users;
1036         else
1037                 count = 0;
1038
1039         if (localdata && !ast_strlen_zero(localdata)){
1040                 /* have var so load it and exit */
1041                 snprintf(val,sizeof(val), "%i",count);
1042                 pbx_builtin_setvar_helper(chan, localdata,val);
1043         } else {
1044                 if (chan->_state != AST_STATE_UP)
1045                         ast_answer(chan);
1046                 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1047         }
1048         LOCAL_USER_REMOVE(u);
1049         return res;
1050 }
1051
1052 /*--- conf_exec: The meetme() application */
1053 static int conf_exec(struct ast_channel *chan, void *data)
1054 {
1055         int res=-1;
1056         struct localuser *u;
1057         char confno[AST_MAX_EXTENSION] = "";
1058         int allowretry = 0;
1059         int retrycnt = 0;
1060         struct ast_conference *cnf;
1061         int confflags = 0;
1062         int dynamic = 0;
1063         int empty = 0, empty_no_pin = 0;
1064         char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
1065
1066         if (!data || ast_strlen_zero(data)) {
1067                 allowretry = 1;
1068                 notdata = "";
1069         } else {
1070                 notdata = data;
1071         }
1072         LOCAL_USER_ADD(u);
1073         if (chan->_state != AST_STATE_UP)
1074                 ast_answer(chan);
1075
1076         info = ast_strdupa((char *)notdata);
1077
1078         if (info) {
1079                 char *tmp = strsep(&info, "|");
1080                 strncpy(confno, tmp, sizeof(confno));
1081                 if (ast_strlen_zero(confno)) {
1082                         allowretry = 1;
1083                 }
1084         }
1085         if (info)
1086                 inflags = strsep(&info, "|");
1087         if (info)
1088                 inpin = strsep(&info, "|");
1089         if (inpin)
1090                 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
1091
1092         if (inflags) {
1093                 if (strchr(inflags, 'a'))
1094                         confflags |= CONFFLAG_ADMIN;
1095                 if (strchr(inflags, 'm'))
1096                         confflags |= CONFFLAG_MONITOR;
1097                 if (strchr(inflags, 'p'))
1098                         confflags |= CONFFLAG_POUNDEXIT;
1099                 if (strchr(inflags, 's'))
1100                         confflags |= CONFFLAG_STARMENU;
1101                 if (strchr(inflags, 't'))
1102                         confflags |= CONFFLAG_TALKER;
1103                 if (strchr(inflags, 'q'))
1104                         confflags |= CONFFLAG_QUIET;
1105                 if (strchr(inflags, 'M'))
1106                         confflags |= CONFFLAG_MOH;
1107                 if (strchr(inflags, 'x'))
1108                         confflags |= CONFFLAG_ADMINEXIT;
1109                 if (strchr(inflags, 'b'))
1110                         confflags |= CONFFLAG_AGI;
1111                 if (strchr(inflags, 'd'))
1112                         dynamic = 1;
1113                 if (strchr(inflags, 'D')) {
1114                         dynamic = 1;
1115                         if (! inpin) {
1116                                 strncpy(the_pin, "q", sizeof(the_pin) - 1);
1117                         }
1118                 }
1119                 if (strchr(inflags, 'e'))
1120                         empty = 1;
1121                 if (strchr(inflags, 'E')) {
1122                         empty = 1;
1123                         empty_no_pin = 1;
1124                 }
1125         }
1126
1127         do {
1128                 if (retrycnt > 3)
1129                         allowretry = 0;
1130                 if (empty) {
1131                         int i, map[1024];
1132                         struct ast_config *cfg;
1133                         struct ast_variable *var;
1134                         int confno_int;
1135
1136                         memset(map, 0, sizeof(map));
1137
1138                         ast_mutex_lock(&conflock);
1139                         cnf = confs;
1140                         while (cnf) {
1141                                 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
1142                                         /* Disqualify in use conference */
1143                                         if (confno_int >= 0 && confno_int < 1024)
1144                                                 map[confno_int]++;
1145                                 }
1146                                 cnf = cnf->next;
1147                         }
1148                         ast_mutex_unlock(&conflock);
1149
1150                         /* Disqualify static conferences with pins */
1151                         cfg = ast_load("meetme.conf");
1152                         if (cfg) {
1153                                 var = ast_variable_browse(cfg, "rooms");
1154                                 while(var) {
1155                                         if (!strcasecmp(var->name, "conf")) {
1156                                                 char *stringp = ast_strdupa(var->value);
1157                                                 if (stringp) {
1158                                                         char *confno_tmp = strsep(&stringp, "|,");
1159                                                         int found = 0;
1160                                                         if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
1161                                                                 if (confno_int >= 0 && confno_int < 1024) {
1162                                                                         if (stringp && empty_no_pin) {
1163                                                                                 map[confno_int]++;
1164                                                                         }
1165                                                                 }
1166                                                         }
1167                                                         if (! dynamic) {
1168                                                                 /* For static:  run through the list and see if this conference is empty */
1169                                                                 ast_mutex_lock(&conflock);
1170                                                                 cnf = confs;
1171                                                                 while (cnf) {
1172                                                                         if (!strcmp(confno_tmp, cnf->confno)) {
1173                                                                                 found = 1;
1174                                                                                 break;
1175                                                                         }
1176                                                                 }
1177                                                                 ast_mutex_unlock(&conflock);
1178                                                                 if (!found) {
1179                                                                         if ((empty_no_pin && (!stringp)) || (!empty_no_pin)) {
1180                                                                                 strncpy(confno, confno_tmp, sizeof(confno) - 1);
1181                                                                                 break;
1182                                                                         }
1183                                                                 }
1184                                                         }
1185                                                 }
1186                                         }
1187                                         var = var->next;
1188                                 }
1189                                 ast_destroy(cfg);
1190                         }
1191
1192                         /* Select first conference number not in use */
1193                         if (dynamic) {
1194                                 for (i=0;i<1024;i++) {
1195                                         if (dynamic && (!map[i])) {
1196                                                 snprintf(confno, sizeof(confno) - 1, "%d", i);
1197                                                 break;
1198                                         }
1199                                 }
1200                         }
1201
1202                         /* Not found? */
1203                         if (ast_strlen_zero(confno)) {
1204                                 res = ast_streamfile(chan, "conf-noempty", chan->language);
1205                                 if (!res)
1206                                         ast_waitstream(chan, "");
1207                         } else {
1208                                 if (sscanf(confno, "%d", &confno_int) == 1) {
1209                                         res = ast_streamfile(chan, "conf-enteringno", chan->language);
1210                                         if (!res) {
1211                                                 ast_waitstream(chan, "");
1212                                                 res = ast_say_digits(chan, confno_int, "", chan->language);
1213                                         }
1214                                 }
1215                         }
1216                 }
1217                 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
1218                         /* Prompt user for conference number */
1219                         res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
1220                         if (res < 0) {
1221                                 /* Don't try to validate when we catch an error */
1222                                 strcpy(confno, "");
1223                                 allowretry = 0;
1224                                 break;
1225                         }
1226                 }
1227                 if (!ast_strlen_zero(confno)) {
1228                         /* Check the validity of the conference */
1229                         cnf = find_conf(chan, confno, 1, dynamic, the_pin);
1230                         if (!cnf) {
1231                                 res = ast_streamfile(chan, "conf-invalid", chan->language);
1232                                 if (!res)
1233                                         ast_waitstream(chan, "");
1234                                 res = -1;
1235                                 if (allowretry)
1236                                         strcpy(confno, "");
1237                         } else {
1238                                 if (!ast_strlen_zero(cnf->pin)) {
1239                                         char pin[AST_MAX_EXTENSION];
1240
1241                                         if (*the_pin) {
1242                                                 strncpy(pin, the_pin, sizeof(pin) - 1);
1243                                                 res = 0;
1244                                         } else {
1245                                                 /* Prompt user for pin if pin is required */
1246                                                 res = ast_app_getdata(chan, "conf-getpin", pin, sizeof(pin) - 1, 0);
1247                                         }
1248                                         if (res >= 0) {
1249                                                 if (!strcasecmp(pin, cnf->pin)) {
1250                                                         /* Pin correct */
1251                                                         allowretry = 0;
1252                                                         /* Run the conference */
1253                                                         res = conf_run(chan, cnf, confflags);
1254                                                 } else {
1255                                                         /* Pin invalid */
1256                                                         res = ast_streamfile(chan, "conf-invalidpin", chan->language);
1257                                                         if (!res)
1258                                                                 ast_waitstream(chan, "");
1259                                                         res = -1;
1260                                                         if (allowretry)
1261                                                                 strcpy(confno, "");
1262                                                 }
1263                                         } else {
1264                                                 res = -1;
1265                                                 allowretry = 0;
1266                                         }
1267                                 } else {
1268                                         /* No pin required */
1269                                         allowretry = 0;
1270
1271                                         /* Run the conference */
1272                                         res = conf_run(chan, cnf, confflags);
1273                                 }
1274                         }
1275                 }
1276         } while (allowretry);
1277         /* Do the conference */
1278         LOCAL_USER_REMOVE(u);
1279         return res;
1280 }
1281
1282 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
1283         struct ast_conf_user *user = NULL;
1284         char usrno[1024] = "";
1285         if (conf && callerident) {
1286                 user = conf->firstuser;
1287                 while(user) {
1288                         sprintf(usrno, "%i", user->user_no);
1289                         if (strcmp(usrno, callerident) == 0)
1290                                 return user;
1291                         user = user->nextuser;
1292                 }
1293         }
1294         return NULL;
1295 }
1296
1297 /*--- admin_exec: The MeetMeadmin application */
1298 /* MeetMeAdmin(confno, command, caller) */
1299 static int admin_exec(struct ast_channel *chan, void *data) {
1300         char *params, *command = NULL, *caller = NULL, *conf = NULL;
1301         struct ast_conference *cnf;
1302         struct ast_conf_user *user = NULL;
1303
1304         ast_mutex_lock(&conflock);
1305         /* The param has the conference number the user and the command to execute */
1306         if (data && !ast_strlen_zero(data)) {           
1307                 params = ast_strdupa((char *) data);
1308                 conf = strsep(&params, "|");
1309                 command = strsep(&params, "|");
1310                 caller = strsep(&params, "|");
1311                 
1312                 ast_mutex_lock(&conflock);
1313                 cnf = confs;
1314                 while (cnf) {
1315                         if (strcmp(cnf->confno, conf) == 0) 
1316                                 break;
1317                         cnf = cnf->next;
1318                 }
1319                 ast_mutex_unlock(&conflock);
1320                 
1321                 if (caller)
1322                         user = find_user(cnf, caller);
1323                 
1324                 if (cnf) {
1325                         switch((int) (*command)) {
1326                                 case 76: /* L: Lock */ 
1327                                         cnf->locked = 1;
1328                                         break;
1329                                 case 108: /* l: Unlock */ 
1330                                         cnf->locked = 0;
1331                                         break;
1332                                 case 75: /* K: kick all users*/
1333                                         user = cnf->firstuser;
1334                                         while(user) {
1335                                                 user->adminflags |= ADMINFLAG_KICKME;
1336                                                 if (user->nextuser) {
1337                                                         user = user->nextuser;
1338                                                 } else {
1339                                                         break;
1340                                                 }
1341                                         }
1342                                         break;
1343                                 case 77: /* M: Mute */ 
1344                                         if (user) {
1345                                                 user->adminflags |= ADMINFLAG_MUTED;
1346                                         } else {
1347                                                 ast_log(LOG_NOTICE, "Specified User not found!");
1348                                         }
1349                                         break;
1350                                 case 109: /* m: Unmute */ 
1351                                         if (user && (user->adminflags & ADMINFLAG_MUTED)) {
1352                                                 user->adminflags ^= ADMINFLAG_MUTED;
1353                                         } else {
1354                                                 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
1355                                         }
1356                                         break;
1357                                 case 107: /* k: Kick user */ 
1358                                         if (user) {
1359                                                 user->adminflags |= ADMINFLAG_KICKME;
1360                                         } else {
1361                                                 ast_log(LOG_NOTICE, "Specified User not found!");
1362                                         }
1363                                         break;
1364                         }
1365                 } else {
1366                         ast_log(LOG_NOTICE, "Conference Number not found\n");
1367                 }
1368         }
1369         ast_mutex_unlock(&conflock);
1370         return 0;
1371 }
1372
1373 int unload_module(void)
1374 {
1375         STANDARD_HANGUP_LOCALUSERS;
1376         ast_cli_unregister(&cli_show_confs);
1377         ast_cli_unregister(&cli_conf);
1378         ast_unregister_application(app3);
1379         ast_unregister_application(app2);
1380         return ast_unregister_application(app);
1381 }
1382
1383 int load_module(void)
1384 {
1385         ast_cli_register(&cli_show_confs);
1386         ast_cli_register(&cli_conf);
1387         ast_register_application(app3, admin_exec, synopsis3, descrip3);
1388         ast_register_application(app2, count_exec, synopsis2, descrip2);
1389         return ast_register_application(app, conf_exec, synopsis, descrip);
1390 }
1391
1392 char *description(void)
1393 {
1394         return tdesc;
1395 }
1396
1397 int usecount(void)
1398 {
1399         int res;
1400         STANDARD_USECOUNT(res);
1401         return res;
1402 }
1403
1404 char *key()
1405 {
1406         return ASTERISK_GPL_KEY;
1407 }