c1b1a215c46bceeaec1037f9823c9c06213cf55f
[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 - 2005, Digium, Inc.
7  *
8  * Mark Spencer <markster@digium.com>
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/dsp.h>
23 #include <asterisk/musiconhold.h>
24 #include <asterisk/manager.h>
25 #include <asterisk/options.h>
26 #include <asterisk/cli.h>
27 #include <asterisk/say.h>
28 #include <asterisk/utils.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <stdlib.h>
34 #include <sys/ioctl.h>
35 #include "../asterisk.h"
36 #include "../astconf.h"
37
38 #ifdef __linux__
39 #include <linux/zaptel.h>
40 #else
41 #include <zaptel.h>
42 #endif /* __linux__ */
43
44 static char *tdesc = "MeetMe conference bridge";
45
46 static char *app = "MeetMe";
47 static char *app2 = "MeetMeCount";
48 static char *app3 = "MeetMeAdmin";
49
50 static char *synopsis = "MeetMe conference bridge";
51 static char *synopsis2 = "MeetMe participant count";
52 static char *synopsis3 = "MeetMe conference Administration";
53
54 static char *descrip =
55 "  MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe conference.\n"
56 "If the conference number is omitted, the user will be prompted to enter\n"
57 "one. \n"
58 "MeetMe returns 0 if user pressed # to exit (see option 'p'), otherwise -1.\n"
59 "Please note: A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING TO WORK!\n\n"
60
61 "The option string may contain zero or more of the following characters:\n"
62 "      'm' -- set monitor only mode (Listen only, no talking)\n"
63 "      't' -- set talk only mode. (Talk only, no listening)\n"
64 "      'T' -- set talker detection (sent to manager interface and meetme list)\n"
65 "      'i' -- announce user join/leave\n"
66 "      'p' -- allow user to exit the conference by pressing '#'\n"
67 "      'X' -- allow user to exit the conference by entering a valid single\n"
68 "             digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
69 "             if that variable is not defined.\n"
70 "      'd' -- dynamically add conference\n"
71 "      'D' -- dynamically add conference, prompting for a PIN\n"
72 "      'e' -- select an empty conference\n"
73 "      'E' -- select an empty pinless conference\n"
74 "      'v' -- video mode\n"
75 "      'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
76 "             using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
77 "             meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is wav.\n"
78 "      'q' -- quiet mode (don't play enter/leave sounds)\n"
79 "      'M' -- enable music on hold when the conference has a single caller\n"
80 "      'x' -- close the conference when last marked user exits\n"
81 "      'w' -- wait until the marked user enters the conference\n"
82 "      'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
83 "         Default: conf-background.agi\n"
84 "        (Note: This does not work with non-Zap channels in the same conference)\n"
85 "      's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
86 "      'a' -- set admin mode\n"
87 "      'A' -- set marked mode\n"
88 "      'P' -- always prompt for the pin even if it is specified\n";
89
90 static char *descrip2 =
91 "  MeetMeCount(confno[|var]): Plays back the number of users in the specifiedi\n"
92 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
93 "will be returned in the variable. Returns 0 on success or -1 on a hangup.\n"
94 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
95
96 static char *descrip3 = 
97 "  MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
98 "      'K' -- Kick all users out of conference\n"
99 "      'k' -- Kick one user out of conference\n"
100 "      'e' -- Eject last user that joined\n"
101 "      'L' -- Lock conference\n"
102 "      'l' -- Unlock conference\n"
103 "      'M' -- Mute conference\n"
104 "      'm' -- Unmute conference\n"
105 "      'N' -- Mute entire conference (except admin)\n"
106 "      'n' -- Unmute entire conference (except admin)\n"
107 "";
108
109 STANDARD_LOCAL_USER;
110
111 LOCAL_USER_DECL;
112
113 static struct ast_conference {
114         char confno[AST_MAX_EXTENSION];         /* Conference */
115         struct ast_channel *chan;       /* Announcements channel */
116         int fd;                         /* Announcements fd */
117         int zapconf;                    /* Zaptel Conf # */
118         int users;                      /* Number of active users */
119         int markedusers;                  /* Number of marked users */
120         struct ast_conf_user *firstuser;  /* Pointer to the first user struct */
121         struct ast_conf_user *lastuser;   /* Pointer to the last user struct */
122         time_t start;                   /* Start time (s) */
123         int recording;                  /* recording status */
124         int isdynamic;                  /* Created on the fly? */
125         int locked;                       /* Is the conference locked? */
126         pthread_t recordthread;         /* thread for recording */
127         pthread_attr_t attr;            /* thread attribute */
128         char *recordingfilename;        /* Filename to record the Conference into */
129         char *recordingformat;          /* Format to record the Conference in */
130         char pin[AST_MAX_EXTENSION];                    /* If protected by a PIN */
131         char pinadmin[AST_MAX_EXTENSION];       /* If protected by a admin PIN */
132         struct ast_conference *next;
133 } *confs;
134
135 struct ast_conf_user {
136         int user_no;                 /* User Number */
137         struct ast_conf_user *prevuser;  /* Pointer to the previous user */
138         struct ast_conf_user *nextuser;  /* Pointer to the next user */
139         int userflags;                   /* Flags as set in the conference */
140         int adminflags;                  /* Flags set by the Admin */
141         struct ast_channel *chan;        /* Connected channel */
142         int talking;                     /* Is user talking */
143         char usrvalue[50];               /* Custom User Value */
144         char namerecloc[AST_MAX_EXTENSION]; /* Name Recorded file Location */
145         time_t jointime;                 /* Time the user joined the conference */
146 };
147
148 #define ADMINFLAG_MUTED (1 << 1)        /* User is muted */
149 #define ADMINFLAG_KICKME (1 << 2)       /* User is kicked */
150 #define MEETME_DELAYDETECTTALK          300
151 #define MEETME_DELAYDETECTENDTALK       1000
152
153 AST_MUTEX_DEFINE_STATIC(conflock);
154
155 static int admin_exec(struct ast_channel *chan, void *data);
156
157 static void *recordthread(void *args);
158
159 #include "enter.h"
160 #include "leave.h"
161
162 #define ENTER   0
163 #define LEAVE   1
164
165 #define MEETME_RECORD_OFF       0
166 #define MEETME_RECORD_ACTIVE    1
167 #define MEETME_RECORD_TERMINATE 2
168
169 #define CONF_SIZE 320
170
171 #define CONFFLAG_ADMIN  (1 << 1)        /* If set the user has admin access on the conference */
172 #define CONFFLAG_MONITOR (1 << 2)       /* If set the user can only receive audio from the conference */
173 #define CONFFLAG_POUNDEXIT (1 << 3)     /* If set asterisk will exit conference when '#' is pressed */
174 #define CONFFLAG_STARMENU (1 << 4)      /* If set asterisk will provide a menu to the user what '*' is pressed */
175 #define CONFFLAG_TALKER (1 << 5)        /* If set the use can only send audio to the conference */
176 #define CONFFLAG_QUIET (1 << 6)         /* If set there will be no enter or leave sounds */
177 #define CONFFLAG_VIDEO (1 << 7)         /* Set to enable video mode */
178 #define CONFFLAG_AGI (1 << 8)           /* Set to run AGI Script in Background */
179 #define CONFFLAG_MOH (1 << 9)           /* Set to have music on hold when user is alone in conference */
180 #define CONFFLAG_MARKEDEXIT (1 << 10)    /* If set the MeetMe will return if all marked with this flag left */
181 #define CONFFLAG_WAITMARKED (1 << 11)   /* If set, the MeetMe will wait until a marked user enters */
182 #define CONFFLAG_EXIT_CONTEXT (1 << 12) /* If set, the MeetMe will exit to the specified context */
183 #define CONFFLAG_MARKEDUSER (1 << 13)   /* If set, the user will be marked */
184 #define CONFFLAG_INTROUSER (1 << 14)    /* If set, user will be ask record name on entry of conference */
185 #define CONFFLAG_RECORDCONF (1<< 15)    /* If set, the MeetMe will be recorded */
186 #define CONFFLAG_MONITORTALKER (1 << 16) /* If set, the user will be monitored if the user is talking or not */
187
188 static char *istalking(int x)
189 {
190         if (x > 0)
191                 return "(talking)";
192         else if (x < 0)
193                 return "(unmonitored)";
194         else 
195                 return "(not talking)";
196 }
197
198 static int careful_write(int fd, unsigned char *data, int len)
199 {
200         int res;
201         int x;
202         while(len) {
203                 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
204                 res = ioctl(fd, ZT_IOMUX, &x);
205                 if (res >= 0)
206                         res = write(fd, data, len);
207                 if (res < 1) {
208                         if (errno != EAGAIN) {
209                                 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
210                                 return -1;
211                         } else
212                                 return 0;
213                 }
214                 len -= res;
215                 data += res;
216         }
217         return 0;
218 }
219
220 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, int sound)
221 {
222         unsigned char *data;
223         int len;
224         int res=-1;
225         if (!chan->_softhangup)
226                 res = ast_autoservice_start(chan);
227         ast_mutex_lock(&conflock);
228         switch(sound) {
229         case ENTER:
230                 data = enter;
231                 len = sizeof(enter);
232                 break;
233         case LEAVE:
234                 data = leave;
235                 len = sizeof(leave);
236                 break;
237         default:
238                 data = NULL;
239                 len = 0;
240         }
241         if (data) 
242                 careful_write(conf->fd, data, len);
243         ast_mutex_unlock(&conflock);
244         if (!res) 
245                 ast_autoservice_stop(chan);
246 }
247
248 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic)
249 {
250         struct ast_conference *cnf;
251         struct zt_confinfo ztc;
252         ast_mutex_lock(&conflock);
253         cnf = confs;
254         while(cnf) {
255                 if (!strcmp(confno, cnf->confno)) 
256                         break;
257                 cnf = cnf->next;
258         }
259         if (!cnf && (make || dynamic)) {
260                 cnf = malloc(sizeof(struct ast_conference));
261                 if (cnf) {
262                         /* Make a new one */
263                         memset(cnf, 0, sizeof(struct ast_conference));
264                         strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1);
265                         strncpy(cnf->pin, pin, sizeof(cnf->pin) - 1);
266                         strncpy(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin) - 1);
267                         cnf->markedusers = 0;
268                         cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo", NULL);
269                         if (cnf->chan) {
270                                 cnf->fd = cnf->chan->fds[0];    /* for use by conf_play() */
271                         } else {
272                                 ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
273                                 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
274                                 if (cnf->fd < 0) {
275                                         ast_log(LOG_WARNING, "Unable to open pseudo device\n");
276                                         free(cnf);
277                                         cnf = NULL;
278                                         goto cnfout;
279                                 }
280                         }
281                         memset(&ztc, 0, sizeof(ztc));
282                         /* Setup a new zap conference */
283                         ztc.chan = 0;
284                         ztc.confno = -1;
285                         ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
286                         if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
287                                 ast_log(LOG_WARNING, "Error setting conference\n");
288                                 if (cnf->chan)
289                                         ast_hangup(cnf->chan);
290                                 else
291                                         close(cnf->fd);
292                                 free(cnf);
293                                 cnf = NULL;
294                                 goto cnfout;
295                         }
296                         /* Fill the conference struct */
297                         cnf->start = time(NULL);
298                         cnf->zapconf = ztc.confno;
299                         cnf->isdynamic = dynamic;
300                         cnf->firstuser = NULL;
301                         cnf->lastuser = NULL;
302                         cnf->locked = 0;
303                         if (option_verbose > 2)
304                                 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
305                         cnf->next = confs;
306                         confs = cnf;
307                 } else  
308                         ast_log(LOG_WARNING, "Out of memory\n");
309         }
310 cnfout:
311         ast_mutex_unlock(&conflock);
312         return cnf;
313 }
314
315 static int confs_show(int fd, int argc, char **argv)
316 {
317         ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
318         return RESULT_SUCCESS;
319 }
320
321 static char show_confs_usage[] =
322 "Deprecated! Please use 'meetme' instead.\n";
323
324 static struct ast_cli_entry cli_show_confs = {
325         { "show", "conferences", NULL }, confs_show,
326         "Show status of conferences", show_confs_usage, NULL };
327         
328 static int conf_cmd(int fd, int argc, char **argv) {
329         /* Process the command */
330         struct ast_conference *cnf;
331         struct ast_conf_user *user;
332         int hr, min, sec;
333         int i = 0, total = 0;
334         time_t now;
335         char *header_format = "%-14s %-14s %-10s %-8s  %-8s\n";
336         char *data_format = "%-12.12s   %4.4d         %4.4s       %02d:%02d:%02d  %-8s\n";
337         char cmdline[1024] = "";
338
339         if (argc > 8)
340                 ast_cli(fd, "Invalid Arguments.\n");
341         /* Check for length so no buffer will overflow... */
342         for (i = 0; i < argc; i++) {
343                 if (strlen(argv[i]) > 100)
344                         ast_cli(fd, "Invalid Arguments.\n");
345         }
346         if (argc == 1) {
347                 /* 'MeetMe': List all the conferences */        
348         now = time(NULL);
349                 cnf = confs;
350                 if (!cnf) {
351                 ast_cli(fd, "No active MeetMe conferences.\n");
352                 return RESULT_SUCCESS;
353         }
354         ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
355                 while(cnf) {
356                         if (cnf->markedusers == 0)
357                                 strncpy(cmdline, "N/A ", sizeof(cmdline) - 1);
358                         else 
359                                 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
360                         hr = (now - cnf->start) / 3600;
361                         min = ((now - cnf->start) % 3600) / 60;
362                         sec = (now - cnf->start) % 60;
363
364                         ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
365
366                         total += cnf->users;    
367                         cnf = cnf->next;
368                 }
369                 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
370                 return RESULT_SUCCESS;
371         }
372         if (argc < 3)
373                 return RESULT_SHOWUSAGE;
374         strncpy(cmdline, argv[2], sizeof(cmdline) - 1); /* Argv 2: conference number */
375         if (strstr(argv[1], "lock")) {  
376                 if (strcmp(argv[1], "lock") == 0) {
377                         /* Lock */
378                         strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
379                 } else {
380                         /* Unlock */
381                         strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
382                 }
383         } else if (strstr(argv[1], "mute")) { 
384                 if (argc < 4)
385                         return RESULT_SHOWUSAGE;
386                 if (strcmp(argv[1], "mute") == 0) {
387                         /* Mute */
388                         if (strcmp(argv[3], "all") == 0) {
389                                  strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
390                         } else {
391                                 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1); 
392                                 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
393                         }
394                 } else {
395                         /* Unmute */
396                         if (strcmp(argv[3], "all") == 0) {
397                                  strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
398                         } else {
399                                 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
400                                 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
401                         }
402                 }
403         } else if (strcmp(argv[1], "kick") == 0) {
404                 if (argc < 4)
405                         return RESULT_SHOWUSAGE;
406                 if (strcmp(argv[3], "all") == 0) {
407                         /* Kick all */
408                         strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
409                 } else {
410                         /* Kick a single user */
411                         strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
412                         strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
413                 }       
414         } else if(strcmp(argv[1], "list") == 0) {
415                 /* List all the users in a conference */
416                 if (!confs) {
417                         ast_cli(fd, "No active conferences.\n");
418                         return RESULT_SUCCESS;  
419                 }
420                 cnf = confs;
421                 /* Find the right conference */
422                 while(cnf) {
423                         if (strcmp(cnf->confno, argv[2]) == 0)
424                                 break;
425                         if (cnf->next) {
426                                 cnf = cnf->next;        
427                         } else {
428                                 ast_cli(fd, "No such conference: %s.\n",argv[2]);
429                                 return RESULT_SUCCESS;
430                         }
431                 }
432                 /* Show all the users */
433                 user = cnf->firstuser;
434                 while(user) {
435                         ast_cli(fd, "User #: %i  Channel: %s %s %s %s %s\n", user->user_no, user->chan->name, (user->userflags & CONFFLAG_ADMIN) ? "(Admin)" : "", (user->userflags & CONFFLAG_MONITOR) ? "(Listen only)" : "", (user->adminflags & ADMINFLAG_MUTED) ? "(Admn Muted)" : "", istalking(user->talking));
436                         user = user->nextuser;
437                 }
438                 ast_cli(fd,"%d users in that conference.\n",cnf->users);
439                 return RESULT_SUCCESS;
440         } else 
441                 return RESULT_SHOWUSAGE;
442         ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
443         admin_exec(NULL, cmdline);
444         return 0;
445 }
446
447 static char *complete_confcmd(char *line, char *word, int pos, int state) {
448         #define CONF_COMMANDS 6
449         int which = 0, x = 0;
450         struct ast_conference *cnf = NULL;
451         struct ast_conf_user *usr = NULL;
452         char *confno = NULL;
453         char usrno[50] = "";
454         char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"};
455         char *myline;
456         
457         if (pos == 1) {
458                 /* Command */
459                 for (x = 0;x < CONF_COMMANDS; x++) {
460                         if (!strncasecmp(cmds[x], word, strlen(word))) {
461                                 if (++which > state) {
462                                         return strdup(cmds[x]);
463                                 }
464                         }
465                 }
466         } else if (pos == 2) {
467                 /* Conference Number */
468                 ast_mutex_lock(&conflock);
469                 cnf = confs;
470                 while(cnf) {
471                         if (!strncasecmp(word, cnf->confno, strlen(word))) {
472                                 if (++which > state)
473                                         break;
474                         }
475                         cnf = cnf->next;
476                 }
477                 ast_mutex_unlock(&conflock);
478                 return cnf ? strdup(cnf->confno) : NULL;
479         } else if (pos == 3) {
480                 /* User Number || Conf Command option*/
481                 if (strstr(line, "mute") || strstr(line, "kick")) {
482                         if ((state == 0) && (strstr(line, "kick") || strstr(line,"mute")) && !(strncasecmp(word, "all", strlen(word)))) {
483                                 return strdup("all");
484                         }
485                         which++;
486                         ast_mutex_lock(&conflock);
487                         cnf = confs;
488
489                         /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
490                         myline = ast_strdupa(line);
491                         if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
492                                 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
493                                         ;
494                         }
495                         
496                         while(cnf) {
497                                 if (strcmp(confno, cnf->confno) == 0) {
498                                         break;
499                                 }
500                                 cnf = cnf->next;
501                         }
502                         if (cnf) {
503                                 /* Search for the user */
504                                 usr = cnf->firstuser;
505                                 while(usr) {
506                                         snprintf(usrno, sizeof(usrno), "%i", usr->user_no);
507                                         if (!strncasecmp(word, usrno, strlen(word))) {
508                                                 if (++which > state)
509                                                         break;
510                                         }
511                                         usr = usr->nextuser;
512                                 }
513                         }
514                         ast_mutex_unlock(&conflock);
515                         return usr ? strdup(usrno) : NULL;
516                 }
517         }
518         return NULL;
519 }
520         
521 static char conf_usage[] =
522 "Usage: meetme  (un)lock|(un)mute|kick|list <confno> <usernumber>\n"
523 "       Executes a command for the conference or on a conferee\n";
524
525 static struct ast_cli_entry cli_conf = {
526         { "meetme", NULL, NULL }, conf_cmd,
527         "Execute a command on a conference or conferee", conf_usage, complete_confcmd };
528
529 static int confnonzero(void *ptr)
530 {
531         struct ast_conference *conf = ptr;
532         int res;
533         ast_mutex_lock(&conflock);
534         res = (conf->markedusers == 0);
535         ast_mutex_unlock(&conflock);
536         return res;
537 }
538
539 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
540 {
541         struct ast_conference *prev=NULL, *cur;
542         struct ast_conf_user *user = malloc(sizeof(struct ast_conf_user));
543         struct ast_conf_user *usr = NULL;
544         int fd;
545         struct zt_confinfo ztc;
546         struct ast_frame *f;
547         struct ast_channel *c;
548         struct ast_frame fr;
549         int outfd;
550         int ms;
551         int nfds;
552         int res;
553         int flags;
554         int retryzap;
555         int origfd;
556         int musiconhold = 0;
557         int firstpass = 0;
558         int origquiet;
559         int ret = -1;
560         int x;
561         int menu_active = 0;
562         int using_pseudo = 0;
563         int duration=20;
564         struct ast_dsp *dsp=NULL;
565
566         struct ast_app *app;
567         char *agifile;
568         char *agifiledefault = "conf-background.agi";
569         char meetmesecs[30] = "";
570         char exitcontext[AST_MAX_EXTENSION] = "";
571         char recordingtmp[AST_MAX_EXTENSION] = "";
572         int dtmf;
573
574         ZT_BUFFERINFO bi;
575         char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
576         char *buf = __buf + AST_FRIENDLY_OFFSET;
577         
578         if (!user) {
579                 ast_log(LOG_ERROR, "Out of memory\n");
580                 return(ret);
581         }
582         memset(user, 0, sizeof(struct ast_conf_user));
583
584         if (confflags & CONFFLAG_RECORDCONF && conf->recording !=MEETME_RECORD_ACTIVE) {
585                 conf->recordingfilename = pbx_builtin_getvar_helper(chan,"MEETME_RECORDINGFILE");
586                 if (!conf->recordingfilename) {
587                         snprintf(recordingtmp,sizeof(recordingtmp),"meetme-conf-rec-%s-%s",conf->confno,chan->uniqueid);
588                         conf->recordingfilename = ast_strdupa(recordingtmp);
589                 }
590                 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
591                 if (!conf->recordingformat) {
592                         snprintf(recordingtmp,sizeof(recordingtmp), "wav");
593                         conf->recordingformat = ast_strdupa(recordingtmp);
594                 }
595                 pthread_attr_init(&conf->attr);
596                 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
597                 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n", conf->confno, conf->recordingfilename, conf->recordingformat);
598                 ast_pthread_create(&conf->recordthread, &conf->attr, recordthread, conf);
599         }
600
601         user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */
602
603         time(&user->jointime);
604
605         if (conf->locked) {
606                 /* Sorry, but this confernce is locked! */      
607                 if (!ast_streamfile(chan, "conf-locked", chan->language))
608                         ast_waitstream(chan, "");
609                 goto outrun;
610         }
611         conf->users++;
612
613         if (confflags & CONFFLAG_MARKEDUSER)
614                 conf->markedusers++;
615       
616         ast_mutex_lock(&conflock);
617         if (conf->firstuser == NULL) {
618                 /* Fill the first new User struct */
619                 user->user_no = 1;
620                 user->nextuser = NULL;
621                 user->prevuser = NULL;
622                 conf->firstuser = user;
623                 conf->lastuser = user;
624         } else {
625                 /* Fill the new user struct */  
626                 user->user_no = conf->lastuser->user_no + 1; 
627                 user->prevuser = conf->lastuser;
628                 user->nextuser = NULL;
629                 if (conf->lastuser->nextuser != NULL) {
630                         ast_log(LOG_WARNING, "Error in User Management!\n");
631                         ast_mutex_unlock(&conflock);
632                         goto outrun;
633                 } else {
634                         conf->lastuser->nextuser = user;
635                         conf->lastuser = user;
636                 }
637         }
638         user->chan = chan;
639         user->userflags = confflags;
640         user->adminflags = 0;
641         user->talking = -1;
642         ast_mutex_unlock(&conflock);
643         origquiet = confflags & CONFFLAG_QUIET;
644         if (confflags & CONFFLAG_EXIT_CONTEXT) {
645                 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) 
646                         strncpy(exitcontext, agifile, sizeof(exitcontext) - 1);
647                 else if (!ast_strlen_zero(chan->macrocontext)) 
648                         strncpy(exitcontext, chan->macrocontext, sizeof(exitcontext) - 1);
649                 else
650                         strncpy(exitcontext, chan->context, sizeof(exitcontext) - 1);
651         }
652         snprintf(user->namerecloc,sizeof(user->namerecloc),"%s/meetme-username-%s-%d",AST_SPOOL_DIR,conf->confno,user->user_no);
653
654         if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER))
655                 ast_record_review(chan,"vm-rec-name",user->namerecloc, 10,"sln", &duration, NULL);
656
657         while((confflags & CONFFLAG_WAITMARKED) && (conf->markedusers == 0)) {
658                 confflags &= ~CONFFLAG_QUIET;
659                 confflags |= origquiet;
660                 /* XXX Announce that we're waiting on the conference lead to join */
661                 if (!(confflags & CONFFLAG_QUIET)) {
662                         res = ast_streamfile(chan, "vm-dialout", chan->language);
663                         if (!res)
664                                 res = ast_waitstream(chan, "");
665                 } else
666                         res = 0;
667                 /* If we're waiting with hold music, set to silent mode */
668                 if (!res) {
669                         confflags |= CONFFLAG_QUIET;
670                         ast_moh_start(chan, NULL);
671                         res = ast_safe_sleep_conditional(chan, 60000, confnonzero, conf);
672                         ast_moh_stop(chan);
673                 }
674                 if (res < 0) {
675                         ast_log(LOG_DEBUG, "Got hangup on '%s' already\n", chan->name);
676                         goto outrun;
677                 }
678         }
679         
680         if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
681                 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
682                         if (ast_waitstream(chan, "") < 0)
683                                 goto outrun;
684                 } else
685                         goto outrun;
686         }
687
688         /* Set it into linear mode (write) */
689         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
690                 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
691                 goto outrun;
692         }
693
694         /* Set it into linear mode (read) */
695         if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
696                 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
697                 goto outrun;
698         }
699         ast_indicate(chan, -1);
700         retryzap = strcasecmp(chan->type, "Zap");
701 zapretry:
702         origfd = chan->fds[0];
703         if (retryzap) {
704                 fd = open("/dev/zap/pseudo", O_RDWR);
705                 if (fd < 0) {
706                         ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
707                         goto outrun;
708                 }
709                 using_pseudo = 1;
710                 /* Make non-blocking */
711                 flags = fcntl(fd, F_GETFL);
712                 if (flags < 0) {
713                         ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
714                         close(fd);
715                         goto outrun;
716                 }
717                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
718                         ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
719                         close(fd);
720                         goto outrun;
721                 }
722                 /* Setup buffering information */
723                 memset(&bi, 0, sizeof(bi));
724                 bi.bufsize = CONF_SIZE/2;
725                 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
726                 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
727                 bi.numbufs = 4;
728                 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
729                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
730                         close(fd);
731                         goto outrun;
732                 }
733                 x = 1;
734                 if (ioctl(fd, ZT_SETLINEAR, &x)) {
735                         ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
736                         close(fd);
737                         goto outrun;
738                 }
739                 nfds = 1;
740         } else {
741                 /* XXX Make sure we're not running on a pseudo channel XXX */
742                 fd = chan->fds[0];
743                 nfds = 0;
744         }
745         memset(&ztc, 0, sizeof(ztc));
746         /* Check to see if we're in a conference... */
747         ztc.chan = 0;   
748         if (ioctl(fd, ZT_GETCONF, &ztc)) {
749                 ast_log(LOG_WARNING, "Error getting conference\n");
750                 close(fd);
751                 goto outrun;
752         }
753         if (ztc.confmode) {
754                 /* Whoa, already in a conference...  Retry... */
755                 if (!retryzap) {
756                         ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
757                         retryzap = 1;
758                         goto zapretry;
759                 }
760         }
761         memset(&ztc, 0, sizeof(ztc));
762         /* Add us to the conference */
763         ztc.chan = 0;   
764         ztc.confno = conf->zapconf;
765         ast_mutex_lock(&conflock);
766         if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER) && conf->users > 1) {
767                 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
768                         if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
769                                 ast_waitstream(conf->chan, "");
770                         if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
771                                 ast_waitstream(conf->chan, "");
772                 }
773         }
774
775         if (confflags & CONFFLAG_MONITOR)
776                 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
777         else if (confflags & CONFFLAG_TALKER)
778                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
779         else 
780                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
781
782         if (ioctl(fd, ZT_SETCONF, &ztc)) {
783                 ast_log(LOG_WARNING, "Error setting conference\n");
784                 close(fd);
785                 ast_mutex_unlock(&conflock);
786                 goto outrun;
787         }
788         ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
789
790         manager_event(EVENT_FLAG_CALL, "MeetmeJoin", 
791                         "Channel: %s\r\n"
792                         "Uniqueid: %s\r\n"
793                         "Meetme: %s\r\n"
794                         "Usernum: %i\r\n",
795                         chan->name, chan->uniqueid, conf->confno, user->user_no);
796
797         if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
798                 firstpass = 1;
799                 if (!(confflags & CONFFLAG_QUIET))
800                         conf_play(chan, conf, ENTER);
801         }
802         ast_mutex_unlock(&conflock);
803         if (confflags & CONFFLAG_AGI) {
804
805                 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
806                   or use default filename of conf-background.agi */
807
808                 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
809                 if (!agifile)
810                         agifile = agifiledefault;
811
812                 if (!strcasecmp(chan->type,"Zap")) {
813                         /*  Set CONFMUTE mode on Zap channel to mute DTMF tones */
814                         x = 1;
815                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
816                 }
817                 /* Find a pointer to the agi app and execute the script */
818                 app = pbx_findapp("agi");
819                 if (app) {
820                         ret = pbx_exec(chan, app, agifile, 1);
821                 } else {
822                         ast_log(LOG_WARNING, "Could not find application (agi)\n");
823                         ret = -2;
824                 }
825                 if (!strcasecmp(chan->type,"Zap")) {
826                         /*  Remove CONFMUTE mode on Zap channel */
827                         x = 0;
828                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
829                 }
830         } else {
831                 if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
832                         /*  Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
833                         x = 1;
834                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
835                 }       
836                 if (confflags &  CONFFLAG_MONITORTALKER && !(dsp = ast_dsp_new())) {
837                         ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
838                         res = -1;
839                 }
840                 for(;;) {
841                         outfd = -1;
842                         ms = -1;
843                         c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
844                         
845                         /* Update the struct with the actual confflags */
846                         user->userflags = confflags;
847                         
848                         /* trying to add moh for single person conf */
849                         if (confflags & CONFFLAG_MOH) {
850                                 if (conf->users == 1) {
851                                         if (musiconhold == 0) {
852                                                 ast_moh_start(chan, NULL);
853                                                 musiconhold = 1;
854                                         } 
855                                 } else {
856                                         if (musiconhold) {
857                                                 ast_moh_stop(chan);
858                                                 musiconhold = 0;
859                                         }
860                                 }
861                         }
862                         
863                         /* Leave if the last marked user left */
864                         if (conf->markedusers == 0 && confflags & CONFFLAG_MARKEDEXIT) {
865                                 ret = -1;
866                                 break;
867                         }
868         
869                         /* Check if the admin changed my modes */
870                         if (user->adminflags) {                 
871                                 /* Set the new modes */
872                                 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
873                                         ztc.confmode ^= ZT_CONF_TALKER;
874                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
875                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
876                                                 ret = -1;
877                                                 break;
878                                         }
879                                 }
880                                 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
881                                         ztc.confmode |= ZT_CONF_TALKER;
882                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
883                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
884                                                 ret = -1;
885                                                 break;
886                                         }
887                                 }
888                                 if (user->adminflags & ADMINFLAG_KICKME) {
889                                         /* You have been kicked. */
890                                         if (!ast_streamfile(chan, "conf-kicked", chan->language))
891                                                 ast_waitstream(chan, "");
892                                         ret = 0;
893                                         break;
894                                 }
895                         } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
896                                 ztc.confmode |= ZT_CONF_TALKER;
897                                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
898                                         ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
899                                         ret = -1;
900                                         break;
901                                 }
902                         }
903
904                         if (c) {
905                                 if (c->fds[0] != origfd) {
906                                         if (using_pseudo) {
907                                                 /* Kill old pseudo */
908                                                 close(fd);
909                                         }
910                                         ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
911                                         retryzap = 0;
912                                         using_pseudo = 0;
913                                         goto zapretry;
914                                 }
915                                 f = ast_read(c);
916                                 if (!f)
917                                         break;
918                                 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
919                                         if (confflags &  CONFFLAG_MONITORTALKER) {
920                                                 int totalsilence;
921                                                 if (user->talking == -1)
922                                                         user->talking = 0;
923
924                                                 res = ast_dsp_silence(dsp, f, &totalsilence);
925                                                 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
926                                                         user->talking = 1;
927                                                         manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
928                                                                 "Channel: %s\r\n"
929                                                                 "Uniqueid: %s\r\n"
930                                                                 "Meetme: %s\r\n"
931                                                                 "Usernum: %i\r\n",
932                                                                 chan->name, chan->uniqueid, conf->confno, user->user_no);
933                                                 }
934                                                 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
935                                                         user->talking = 0;
936                                                         manager_event(EVENT_FLAG_CALL, "MeetmeStopTalking",
937                                                                 "Channel: %s\r\n"
938                                                                 "Uniqueid: %s\r\n"
939                                                                 "Meetme: %s\r\n"
940                                                                 "Usernum: %i\r\n",
941                                                                 chan->name, chan->uniqueid, conf->confno, user->user_no);
942                                                 }
943                                         }
944                                         if (using_pseudo) {
945                                                 /* Carefully write */
946                                                 careful_write(fd, f->data, f->datalen);
947                                         }
948                                 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
949                                         char tmp[2];
950                                         tmp[0] = f->subclass;
951                                         tmp[1] = '\0';
952                                         if (ast_exists_extension(chan, exitcontext, tmp, 1, chan->cid.cid_num)) {
953                                                 strncpy(chan->context, exitcontext, sizeof(chan->context) - 1);
954                                                 strncpy(chan->exten, tmp, sizeof(chan->exten) - 1);
955                                                 chan->priority = 0;
956                                                 ret = 0;
957                                                 break;
958                                         }
959                                 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
960                                         ret = 0;
961                                         break;
962                                 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
963                                         if (musiconhold) {
964                                                 ast_moh_stop(chan);
965                                         }
966                                         if ((confflags & CONFFLAG_ADMIN)) {
967                                                 /* Admin menu */
968                                                 if (!menu_active) {
969                                                         menu_active = 1;
970                                                         /* Record this sound! */
971                                                          if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
972                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
973                                                         else 
974                                                                 dtmf = 0;
975                                                 } else 
976                                                         dtmf = f->subclass;
977                                                 if (dtmf) {
978                                                         switch(dtmf) {
979                                                                 case '1': /* Un/Mute */
980                                                                         menu_active = 0;
981                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
982                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
983                                                                         confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
984                                                                         } else {
985                                                                                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
986                                                                                 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
987                                                                         }
988                                                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
989                                                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
990                                                                                 ret = -1;
991                                                                                 break;
992                                                                         }
993                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
994                                                                                 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
995                                                                                         ast_waitstream(chan, "");
996                                                                         } else {
997                                                                                 if (!ast_streamfile(chan, "conf-muted", chan->language))
998                                                                                         ast_waitstream(chan, "");
999                                                                         }
1000                                                                         break;
1001                                                                 case '2': /* Un/Lock the Conference */
1002                                                                         menu_active = 0;
1003                                                                         if (conf->locked) {
1004                                                                                 conf->locked = 0;
1005                                                                                 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1006                                                                                         ast_waitstream(chan, "");
1007                                                                         } else {
1008                                                                                 conf->locked = 1;
1009                                                                                 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1010                                                                                         ast_waitstream(chan, "");
1011                                                                         }
1012                                                                         break;
1013                                                                 case '3': /* Eject last user */
1014                                                                         menu_active = 0;
1015                                                                         usr = conf->lastuser;
1016                                                                         if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1017                                                                                 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1018                                                                                         ast_waitstream(chan, "");
1019                                                                         } else 
1020                                                                                 usr->adminflags |= ADMINFLAG_KICKME;
1021                                                                         ast_stopstream(chan);
1022                                                                         break;  
1023                                                                 default:
1024                                                                         menu_active = 0;
1025                                                                         /* Play an error message! */
1026                                                                         if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1027                                                                                 ast_waitstream(chan, "");
1028                                                                         break;
1029                                                         }
1030                                                 }
1031                                         } else {
1032                                                 /* User menu */
1033                                                 if (!menu_active) {
1034                                                         menu_active = 1;
1035                                                         /* Record this sound! */
1036                                                         if (!ast_streamfile(chan, "conf-usermenu", chan->language))
1037                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1038                                                         else
1039                                                                 dtmf = 0;
1040                                                 } else 
1041                                                         dtmf = f->subclass;
1042                                                 if (dtmf) {
1043                                                         switch(dtmf) {
1044                                                                 case '1': /* Un/Mute */
1045                                                                         menu_active = 0;
1046                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
1047                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1048                                                                         confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1049                                                                         } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
1050                                                                                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1051                                                                                 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1052                                                                         }
1053                                                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1054                                                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1055                                                                                 ret = -1;
1056                                                                                 break;
1057                                                                         }
1058                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
1059                                                                                 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1060                                                                                         ast_waitstream(chan, "");
1061                                                                         } else {
1062                                                                                 if (!ast_streamfile(chan, "conf-muted", chan->language))
1063                                                                                         ast_waitstream(chan, "");
1064                                                                         }
1065                                                                         break;
1066                                                                 default:
1067                                                                         menu_active = 0;
1068                                                                         /* Play an error message! */
1069                                                                         if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1070                                                                                 ast_waitstream(chan, "");
1071                                                                         break;
1072                                                         }
1073                                                 }
1074                                         }
1075                                         if (musiconhold) {
1076                                                 ast_moh_start(chan, NULL);
1077                                         }
1078                                 } else if (option_debug) {
1079                                         ast_log(LOG_DEBUG, "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",chan->name,f->frametype,f->subclass);
1080                                 }
1081                                 ast_frfree(f);
1082                         } else if (outfd > -1) {
1083                                 res = read(outfd, buf, CONF_SIZE);
1084                                 if (res > 0) {
1085                                         memset(&fr, 0, sizeof(fr));
1086                                         fr.frametype = AST_FRAME_VOICE;
1087                                         fr.subclass = AST_FORMAT_SLINEAR;
1088                                         fr.datalen = res;
1089                                         fr.samples = res/2;
1090                                         fr.data = buf;
1091                                         fr.offset = AST_FRIENDLY_OFFSET;
1092                                         if (ast_write(chan, &fr) < 0) {
1093                                                 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1094                                                 /* break; */
1095                                         }
1096                                 } else 
1097                                         ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1098                         }
1099                 }
1100         }
1101         if (using_pseudo)
1102                 close(fd);
1103         else {
1104                 /* Take out of conference */
1105                 /* Add us to the conference */
1106                 ztc.chan = 0;   
1107                 ztc.confno = 0;
1108                 ztc.confmode = 0;
1109                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1110                         ast_log(LOG_WARNING, "Error setting conference\n");
1111                 }
1112         }
1113
1114         ast_mutex_lock(&conflock);
1115         if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1116                 conf_play(chan, conf, LEAVE);
1117
1118         if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
1119                 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
1120                         if ((conf->chan) && (conf->users > 1)) {
1121                                 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1122                                         ast_waitstream(conf->chan, "");
1123                                 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1124                                         ast_waitstream(conf->chan, "");
1125                         }
1126                         ast_filedelete(user->namerecloc, NULL);
1127                 }
1128         }
1129         ast_mutex_unlock(&conflock);
1130
1131
1132 outrun:
1133         ast_mutex_lock(&conflock);
1134         if (confflags &  CONFFLAG_MONITORTALKER && dsp)
1135                 ast_dsp_free(dsp);
1136         
1137         if (user->user_no) { /* Only cleanup users who really joined! */
1138                 manager_event(EVENT_FLAG_CALL, "MeetmeLeave", 
1139                         "Channel: %s\r\n"
1140                         "Uniqueid: %s\r\n"
1141                         "Meetme: %s\r\n"
1142                         "Usernum: %i\r\n",
1143                         chan->name, chan->uniqueid, conf->confno, user->user_no);
1144                 prev = NULL;
1145                 conf->users--;
1146                 if (confflags & CONFFLAG_MARKEDUSER) 
1147                         conf->markedusers--;
1148                 cur = confs;
1149                 if (!conf->users) {
1150                         /* No more users -- close this one out */
1151                         while(cur) {
1152                                 if (cur == conf) {
1153                                         if (prev)
1154                                                 prev->next = conf->next;
1155                                         else
1156                                                 confs = conf->next;
1157                                         break;
1158                                 }
1159                                 prev = cur;
1160                                 cur = cur->next;
1161                         }
1162                         if (!cur) 
1163                                 ast_log(LOG_WARNING, "Conference not found\n");
1164                         if (conf->recording == MEETME_RECORD_ACTIVE) {
1165                                 conf->recording = MEETME_RECORD_TERMINATE;
1166                                 ast_mutex_unlock(&conflock);
1167                                 while (1) {
1168                                         ast_mutex_lock(&conflock);
1169                                         if (conf->recording == MEETME_RECORD_OFF)
1170                                                 break;
1171                                         ast_mutex_unlock(&conflock);
1172                                 }
1173                         }
1174                         if (conf->chan)
1175                                 ast_hangup(conf->chan);
1176                         else
1177                                 close(conf->fd);
1178                         free(conf);
1179                 } else {
1180                         /* Remove the user struct */ 
1181                         if (user == conf->firstuser) {
1182                                 if (user->nextuser) {
1183                                         /* There is another entry */
1184                                         user->nextuser->prevuser = NULL;
1185                                 } else {
1186                                         /* We are the only entry */
1187                                         conf->lastuser = NULL;
1188                                 }
1189                                 /* In either case */
1190                                 conf->firstuser = user->nextuser;
1191                         } else if (user == conf->lastuser){
1192                                 if (user->prevuser)
1193                                         user->prevuser->nextuser = NULL;
1194                                 else
1195                                         ast_log(LOG_ERROR, "Bad bad bad!  We're the last, not the first, but nobody before us??\n");
1196                                 conf->lastuser = user->prevuser;
1197                         } else {
1198                                 if (user->nextuser)
1199                                         user->nextuser->prevuser = user->prevuser;
1200                                 else
1201                                         ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
1202                                 if (user->prevuser)
1203                                         user->prevuser->nextuser = user->nextuser;
1204                                 else
1205                                         ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
1206                         }
1207                 }
1208                 /* Return the number of seconds the user was in the conf */
1209                 snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (time(NULL) - user->jointime));
1210                 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1211         }
1212         free(user);
1213         ast_mutex_unlock(&conflock);
1214         return ret;
1215 }
1216
1217 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
1218 {
1219         struct ast_config *cfg;
1220         struct ast_variable *var;
1221         struct ast_conference *cnf;
1222
1223         /* Check first in the conference list */
1224         ast_mutex_lock(&conflock);
1225         cnf = confs;
1226         while (cnf) {
1227                 if (!strcmp(confno, cnf->confno)) 
1228                         break;
1229                 cnf = cnf->next;
1230         }
1231         ast_mutex_unlock(&conflock);
1232
1233         if (!cnf) {
1234                 if (dynamic) {
1235                         /* No need to parse meetme.conf */
1236                         ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1237                         if (dynamic_pin) {
1238                                 if (dynamic_pin[0] == 'q') {
1239                                         /* Query the user to enter a PIN */
1240                                         ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
1241                                 }
1242                                 cnf = build_conf(confno, dynamic_pin, "", make, dynamic);
1243                         } else {
1244                                 cnf = build_conf(confno, "", "", make, dynamic);
1245                         }
1246                 } else {
1247                         /* Check the config */
1248                         cfg = ast_config_load("meetme.conf");
1249                         if (!cfg) {
1250                                 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
1251                                 return NULL;
1252                         }
1253                         var = ast_variable_browse(cfg, "rooms");
1254                         while(var) {
1255                                 if (!strcasecmp(var->name, "conf")) {
1256                                         /* Separate the PIN */
1257                                         char *pin, *pinadmin, *conf;
1258
1259                                         if ((pinadmin = ast_strdupa(var->value))) {
1260                                                 conf = strsep(&pinadmin, "|,");
1261                                                 pin = strsep(&pinadmin, "|,");
1262                                                 if (!strcasecmp(conf, confno)) {
1263                                                         /* Bingo it's a valid conference */
1264                                                         if (pin)
1265                                                                 if (pinadmin)
1266                                                                         cnf = build_conf(confno, pin, pinadmin, make, dynamic);
1267                                                                 else
1268                                                                         cnf = build_conf(confno, pin, "", make, dynamic);
1269                                                         else
1270                                                                 if (pinadmin)
1271                                                                         cnf = build_conf(confno, "", pinadmin, make, dynamic);
1272                                                                 else
1273                                                                         cnf = build_conf(confno, "", "", make, dynamic);
1274                                                         break;
1275                                                 }
1276                                         }
1277                                 }
1278                                 var = var->next;
1279                         }
1280                         if (!var) {
1281                                 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1282                         }
1283                         ast_config_destroy(cfg);
1284                 }
1285         } else if (dynamic_pin) {
1286                 /* Correct for the user selecting 'D' instead of 'd' to have
1287                    someone join into a conference that has already been created
1288                    with a pin. */
1289                 if (dynamic_pin[0] == 'q')
1290                         dynamic_pin[0] = '\0';
1291         }
1292         return cnf;
1293 }
1294
1295 /*--- count_exec: The MeetmeCount application */
1296 static int count_exec(struct ast_channel *chan, void *data)
1297 {
1298         struct localuser *u;
1299         int res = 0;
1300         struct ast_conference *conf;
1301         int count;
1302         char *confnum, *localdata;
1303         char val[80] = "0"; 
1304
1305         if (!data || ast_strlen_zero(data)) {
1306                 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1307                 return -1;
1308         }
1309         localdata = ast_strdupa(data);
1310         LOCAL_USER_ADD(u);
1311         confnum = strsep(&localdata,"|");       
1312         conf = find_conf(chan, confnum, 0, 0, NULL);
1313         if (conf)
1314                 count = conf->users;
1315         else
1316                 count = 0;
1317
1318         if (localdata && !ast_strlen_zero(localdata)){
1319                 /* have var so load it and exit */
1320                 snprintf(val,sizeof(val), "%i",count);
1321                 pbx_builtin_setvar_helper(chan, localdata,val);
1322         } else {
1323                 if (chan->_state != AST_STATE_UP)
1324                         ast_answer(chan);
1325                 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1326         }
1327         LOCAL_USER_REMOVE(u);
1328         return res;
1329 }
1330
1331 /*--- conf_exec: The meetme() application */
1332 static int conf_exec(struct ast_channel *chan, void *data)
1333 {
1334         int res=-1;
1335         struct localuser *u;
1336         char confno[AST_MAX_EXTENSION] = "";
1337         int allowretry = 0;
1338         int retrycnt = 0;
1339         struct ast_conference *cnf;
1340         int confflags = 0;
1341         int dynamic = 0;
1342         int empty = 0, empty_no_pin = 0;
1343         int always_prompt = 0;
1344         char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
1345
1346         if (!data || ast_strlen_zero(data)) {
1347                 allowretry = 1;
1348                 notdata = "";
1349         } else {
1350                 notdata = data;
1351         }
1352         LOCAL_USER_ADD(u);
1353         if (chan->_state != AST_STATE_UP)
1354                 ast_answer(chan);
1355
1356         info = ast_strdupa((char *)notdata);
1357
1358         if (info) {
1359                 char *tmp = strsep(&info, "|");
1360                 strncpy(confno, tmp, sizeof(confno) - 1);
1361                 if (ast_strlen_zero(confno)) {
1362                         allowretry = 1;
1363                 }
1364         }
1365         if (info)
1366                 inflags = strsep(&info, "|");
1367         if (info)
1368                 inpin = strsep(&info, "|");
1369         if (inpin)
1370                 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
1371
1372         if (inflags) {
1373                 if (strchr(inflags, 'a'))
1374                         confflags |= CONFFLAG_ADMIN;
1375                 if (strchr(inflags, 'T'))
1376                         confflags |= CONFFLAG_MONITORTALKER;
1377                 if (strchr(inflags, 'i'))
1378                         confflags |= CONFFLAG_INTROUSER;
1379                 if (strchr(inflags, 'm'))
1380                         confflags |= CONFFLAG_MONITOR;
1381                 if (strchr(inflags, 'p'))
1382                         confflags |= CONFFLAG_POUNDEXIT;
1383                 if (strchr(inflags, 's'))
1384                         confflags |= CONFFLAG_STARMENU;
1385                 if (strchr(inflags, 't'))
1386                         confflags |= CONFFLAG_TALKER;
1387                 if (strchr(inflags, 'q'))
1388                         confflags |= CONFFLAG_QUIET;
1389                 if (strchr(inflags, 'M'))
1390                         confflags |= CONFFLAG_MOH;
1391                 if (strchr(inflags, 'x'))
1392                         confflags |= CONFFLAG_MARKEDEXIT;
1393                 if (strchr(inflags, 'X'))
1394                         confflags |= CONFFLAG_EXIT_CONTEXT;
1395                 if (strchr(inflags, 'A'))
1396                         confflags |= CONFFLAG_MARKEDUSER;
1397                 if (strchr(inflags, 'b'))
1398                         confflags |= CONFFLAG_AGI;
1399                 if (strchr(inflags, 'w'))
1400                         confflags |= CONFFLAG_WAITMARKED;
1401                 if (strchr(inflags, 'r'))
1402                         confflags |= CONFFLAG_RECORDCONF;       
1403                 if (strchr(inflags, 'd'))
1404                         dynamic = 1;
1405                 if (strchr(inflags, 'D')) {
1406                         dynamic = 1;
1407                         if (! inpin) {
1408                                 strncpy(the_pin, "q", sizeof(the_pin) - 1);
1409                         }
1410                 }
1411                 if (strchr(inflags, 'e'))
1412                         empty = 1;
1413                 if (strchr(inflags, 'E')) {
1414                         empty = 1;
1415                         empty_no_pin = 1;
1416                 }
1417                 if (strchr(inflags, 'P'))
1418                         always_prompt = 1;
1419         }
1420
1421         do {
1422                 if (retrycnt > 3)
1423                         allowretry = 0;
1424                 if (empty) {
1425                         int i, map[1024];
1426                         struct ast_config *cfg;
1427                         struct ast_variable *var;
1428                         int confno_int;
1429
1430                         memset(map, 0, sizeof(map));
1431
1432                         ast_mutex_lock(&conflock);
1433                         cnf = confs;
1434                         while (cnf) {
1435                                 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
1436                                         /* Disqualify in use conference */
1437                                         if (confno_int >= 0 && confno_int < 1024)
1438                                                 map[confno_int]++;
1439                                 }
1440                                 cnf = cnf->next;
1441                         }
1442                         ast_mutex_unlock(&conflock);
1443
1444                         /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
1445                         if ((empty_no_pin) || (!dynamic)) {
1446                                 cfg = ast_config_load("meetme.conf");
1447                                 if (cfg) {
1448                                         var = ast_variable_browse(cfg, "rooms");
1449                                         while(var) {
1450                                                 if (!strcasecmp(var->name, "conf")) {
1451                                                         char *stringp = ast_strdupa(var->value);
1452                                                         if (stringp) {
1453                                                                 char *confno_tmp = strsep(&stringp, "|,");
1454                                                                 int found = 0;
1455                                                                 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
1456                                                                         if ((confno_int >= 0) && (confno_int < 1024)) {
1457                                                                                 if (stringp && empty_no_pin) {
1458                                                                                         map[confno_int]++;
1459                                                                                 }
1460                                                                         }
1461                                                                 }
1462                                                                 if (! dynamic) {
1463                                                                         /* For static:  run through the list and see if this conference is empty */
1464                                                                         ast_mutex_lock(&conflock);
1465                                                                         cnf = confs;
1466                                                                         while (cnf) {
1467                                                                                 if (!strcmp(confno_tmp, cnf->confno)) {
1468                                                                                         /* The conference exists, therefore it's not empty */
1469                                                                                         found = 1;
1470                                                                                         break;
1471                                                                                 }
1472                                                                                 cnf = cnf->next;
1473                                                                         }
1474                                                                         ast_mutex_unlock(&conflock);
1475                                                                         if (!found) {
1476                                                                                 /* At this point, we have a confno_tmp (static conference) that is empty */
1477                                                                                 if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) {
1478                                                                                 /* Case 1:  empty_no_pin and pin is nonexistant (NULL)
1479                                                                                  * Case 2:  empty_no_pin and pin is blank (but not NULL)
1480                                                                                  * Case 3:  not empty_no_pin
1481                                                                                  */
1482                                                                                         strncpy(confno, confno_tmp, sizeof(confno) - 1);
1483                                                                                         break;
1484                                                                                         /* XXX the map is not complete (but we do have a confno) */
1485                                                                                 }
1486                                                                         }
1487                                                                 }
1488                                                         } else {
1489                                                                 ast_log(LOG_ERROR, "Out of memory\n");
1490                                                         }
1491                                                 }
1492                                                 var = var->next;
1493                                         }
1494                                         ast_config_destroy(cfg);
1495                                 }
1496                         }
1497                         /* Select first conference number not in use */
1498                         if (ast_strlen_zero(confno) && dynamic) {
1499                                 for (i=0;i<1024;i++) {
1500                                         if (!map[i]) {
1501                                                 snprintf(confno, sizeof(confno), "%d", i);
1502                                                 break;
1503                                         }
1504                                 }
1505                         }
1506
1507                         /* Not found? */
1508                         if (ast_strlen_zero(confno)) {
1509                                 res = ast_streamfile(chan, "conf-noempty", chan->language);
1510                                 if (!res)
1511                                         ast_waitstream(chan, "");
1512                         } else {
1513                                 if (sscanf(confno, "%d", &confno_int) == 1) {
1514                                         res = ast_streamfile(chan, "conf-enteringno", chan->language);
1515                                         if (!res) {
1516                                                 ast_waitstream(chan, "");
1517                                                 res = ast_say_digits(chan, confno_int, "", chan->language);
1518                                         }
1519                                 } else {
1520                                         ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
1521                                 }
1522                         }
1523                 }
1524                 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
1525                         /* Prompt user for conference number */
1526                         res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
1527                         if (res < 0) {
1528                                 /* Don't try to validate when we catch an error */
1529                                 confno[0] = '\0';
1530                                 allowretry = 0;
1531                                 break;
1532                         }
1533                 }
1534                 if (!ast_strlen_zero(confno)) {
1535                         /* Check the validity of the conference */
1536                         cnf = find_conf(chan, confno, 1, dynamic, the_pin);
1537                         if (!cnf) {
1538                                 res = ast_streamfile(chan, "conf-invalid", chan->language);
1539                                 if (!res)
1540                                         ast_waitstream(chan, "");
1541                                 res = -1;
1542                                 if (allowretry)
1543                                         confno[0] = '\0';
1544                         } else {
1545                                 if ((!ast_strlen_zero(cnf->pin) && ! (confflags & CONFFLAG_ADMIN)) || (!ast_strlen_zero(cnf->pinadmin) && (confflags & CONFFLAG_ADMIN))) {
1546                                         char pin[AST_MAX_EXTENSION]="";
1547                                         int j;
1548
1549                                         /* Allow the pin to be retried up to 3 times */
1550                                         for (j=0; j<3; j++) {
1551                                                 if (*the_pin && (always_prompt==0)) {
1552                                                         strncpy(pin, the_pin, sizeof(pin) - 1);
1553                                                         res = 0;
1554                                                 } else {
1555                                                         /* Prompt user for pin if pin is required */
1556                                                         res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
1557                                                 }
1558                                                 if (res >= 0) {
1559                                                         if (!strcasecmp(pin, cnf->pin)  || (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))) {
1560
1561                                                                 /* Pin correct */
1562                                                                 allowretry = 0;
1563                                                                 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) 
1564                                                                         confflags |= CONFFLAG_ADMIN;
1565                                                                 /* Run the conference */
1566                                                                 res = conf_run(chan, cnf, confflags);
1567                                                                 break;
1568                                                         } else {
1569                                                                 /* Pin invalid */
1570                                                                 res = ast_streamfile(chan, "conf-invalidpin", chan->language);
1571                                                                 if (!res)
1572                                                                         ast_waitstream(chan, AST_DIGIT_ANY);
1573                                                                 if (res < 0)
1574                                                                         break;
1575                                                                 pin[0] = res;
1576                                                                 pin[1] = '\0';
1577                                                                 res = -1;
1578                                                                 if (allowretry)
1579                                                                         confno[0] = '\0';
1580                                                         }
1581                                                 } else {
1582                                                         res = -1;
1583                                                         allowretry = 0;
1584                                                         break;
1585                                                 }
1586
1587                                                 /* Don't retry pin with a static pin */
1588                                                 if (*the_pin && (always_prompt==0)) {
1589                                                         break;
1590                                                 }
1591                                         }
1592                                 } else {
1593                                         /* No pin required */
1594                                         allowretry = 0;
1595
1596                                         /* Run the conference */
1597                                         res = conf_run(chan, cnf, confflags);
1598                                 }
1599                         }
1600                 }
1601         } while (allowretry);
1602         /* Do the conference */
1603         LOCAL_USER_REMOVE(u);
1604         return res;
1605 }
1606
1607 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
1608         struct ast_conf_user *user = NULL;
1609         char usrno[1024] = "";
1610         if (conf && callerident) {
1611                 user = conf->firstuser;
1612                 while(user) {
1613                         snprintf(usrno, sizeof(usrno), "%i", user->user_no);
1614                         if (strcmp(usrno, callerident) == 0)
1615                                 return user;
1616                         user = user->nextuser;
1617                 }
1618         }
1619         return NULL;
1620 }
1621
1622 /*--- admin_exec: The MeetMeadmin application */
1623 /* MeetMeAdmin(confno, command, caller) */
1624 static int admin_exec(struct ast_channel *chan, void *data) {
1625         char *params, *command = NULL, *caller = NULL, *conf = NULL;
1626         struct ast_conference *cnf;
1627         struct ast_conf_user *user = NULL;
1628
1629         ast_mutex_lock(&conflock);
1630         /* The param has the conference number the user and the command to execute */
1631         if (data && !ast_strlen_zero(data)) {           
1632                 params = ast_strdupa((char *) data);
1633                 conf = strsep(&params, "|");
1634                 command = strsep(&params, "|");
1635                 caller = strsep(&params, "|");
1636                 
1637                 if (!command) {
1638                         ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
1639                         ast_mutex_unlock(&conflock);
1640                         return -1;
1641                 }
1642                 cnf = confs;
1643                 while (cnf) {
1644                         if (strcmp(cnf->confno, conf) == 0) 
1645                                 break;
1646                         cnf = cnf->next;
1647                 }
1648                 
1649                 if (caller)
1650                         user = find_user(cnf, caller);
1651                 
1652                 if (cnf) {
1653                         switch((int) (*command)) {
1654                                 case 76: /* L: Lock */ 
1655                                         cnf->locked = 1;
1656                                         break;
1657                                 case 108: /* l: Unlock */ 
1658                                         cnf->locked = 0;
1659                                         break;
1660                                 case 75: /* K: kick all users*/
1661                                         user = cnf->firstuser;
1662                                         while(user) {
1663                                                 user->adminflags |= ADMINFLAG_KICKME;
1664                                                 if (user->nextuser) {
1665                                                         user = user->nextuser;
1666                                                 } else {
1667                                                         break;
1668                                                 }
1669                                         }
1670                                         break;
1671                                 case 101: /* e: Eject last user*/
1672                                         user = cnf->lastuser;
1673                                         if (!(user->userflags & CONFFLAG_ADMIN)) {
1674                                                 user->adminflags |= ADMINFLAG_KICKME;
1675                                                 break;
1676                                         } else
1677                                                 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
1678                                         break;
1679                                 case 77: /* M: Mute */ 
1680                                         if (user) {
1681                                                 user->adminflags |= ADMINFLAG_MUTED;
1682                                         } else {
1683                                                 ast_log(LOG_NOTICE, "Specified User not found!\n");
1684                                         }
1685                                         break;
1686                                 case 78: /* N: Mute all users */
1687                                         user = cnf->firstuser;
1688                                         while(user) {
1689                                                 if (user && !(user->userflags & CONFFLAG_ADMIN))
1690                                                         user->adminflags |= ADMINFLAG_MUTED;
1691                                                 if (user->nextuser) {
1692                                                         user = user->nextuser;
1693                                                 } else {
1694                                                         break;
1695                                                 }
1696                                         }
1697                                         break;                                  
1698                                 case 109: /* m: Unmute */ 
1699                                         if (user && (user->adminflags & ADMINFLAG_MUTED)) {
1700                                                 user->adminflags ^= ADMINFLAG_MUTED;
1701                                         } else {
1702                                                 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
1703                                         }
1704                                         break;
1705                                 case  110: /* n: Unmute all users */
1706                                         user = cnf->firstuser;
1707                                         while(user) {
1708                                                 if (user && (user-> adminflags & ADMINFLAG_MUTED)) {
1709                                                         user->adminflags ^= ADMINFLAG_MUTED;
1710                                                 }
1711                                                 if (user->nextuser) {
1712                                                         user = user->nextuser;
1713                                                 } else {
1714                                                         break;
1715                                                 }
1716                                         }
1717                                         break;
1718                                 case 107: /* k: Kick user */ 
1719                                         if (user) {
1720                                                 user->adminflags |= ADMINFLAG_KICKME;
1721                                         } else {
1722                                                 ast_log(LOG_NOTICE, "Specified User not found!");
1723                                         }
1724                                         break;
1725                         }
1726                 } else {
1727                         ast_log(LOG_NOTICE, "Conference Number not found\n");
1728                 }
1729         }
1730         ast_mutex_unlock(&conflock);
1731         return 0;
1732 }
1733
1734 static void *recordthread(void *args)
1735 {
1736         struct ast_conference *cnf;
1737         struct ast_frame *f=NULL;
1738         int flags;
1739         struct ast_filestream *s;
1740         int res=0;
1741
1742         cnf = (struct ast_conference *)args;
1743         if( !cnf || !cnf->chan ) {
1744                 pthread_exit(0);
1745         }
1746         ast_stopstream(cnf->chan);
1747         flags = O_CREAT|O_TRUNC|O_WRONLY;
1748         s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
1749
1750         if (s) {
1751                 cnf->recording = MEETME_RECORD_ACTIVE;
1752                 while (ast_waitfor(cnf->chan, -1) > -1) {
1753                         f = ast_read(cnf->chan);
1754                         if (!f) {
1755                                 res = -1;
1756                                 break;
1757                         }
1758                         if (f->frametype == AST_FRAME_VOICE) {
1759                                 res = ast_writestream(s, f);
1760                                 if (res) 
1761                                         break;
1762                         }
1763                         ast_frfree(f);
1764                         if (cnf->recording == MEETME_RECORD_TERMINATE) {
1765                                 ast_mutex_lock(&conflock);
1766                                 ast_mutex_unlock(&conflock);
1767                                 break;
1768                         }
1769                 }
1770                 cnf->recording = MEETME_RECORD_OFF;
1771                 ast_closestream(s);
1772         }
1773         pthread_exit(0);
1774 }
1775
1776 int unload_module(void)
1777 {
1778         STANDARD_HANGUP_LOCALUSERS;
1779         ast_cli_unregister(&cli_show_confs);
1780         ast_cli_unregister(&cli_conf);
1781         ast_unregister_application(app3);
1782         ast_unregister_application(app2);
1783         return ast_unregister_application(app);
1784 }
1785
1786 int load_module(void)
1787 {
1788         ast_cli_register(&cli_show_confs);
1789         ast_cli_register(&cli_conf);
1790         ast_register_application(app3, admin_exec, synopsis3, descrip3);
1791         ast_register_application(app2, count_exec, synopsis2, descrip2);
1792         return ast_register_application(app, conf_exec, synopsis, descrip);
1793 }
1794
1795
1796 char *description(void)
1797 {
1798         return tdesc;
1799 }
1800
1801 int usecount(void)
1802 {
1803         int res;
1804         STANDARD_USECOUNT(res);
1805         return res;
1806 }
1807
1808 char *key()
1809 {
1810         return ASTERISK_GPL_KEY;
1811 }
1812