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