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