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