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