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