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