Fix silly typo (bug #3646)
[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                 return RESULT_SUCCESS;
439         } else 
440                 return RESULT_SHOWUSAGE;
441         ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
442         admin_exec(NULL, cmdline);
443         return 0;
444 }
445
446 static char *complete_confcmd(char *line, char *word, int pos, int state) {
447         #define CONF_COMMANDS 6
448         int which = 0, x = 0;
449         struct ast_conference *cnf = NULL;
450         struct ast_conf_user *usr = NULL;
451         char *confno = NULL;
452         char usrno[50] = "";
453         char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"};
454         char *myline;
455         
456         if (pos == 1) {
457                 /* Command */
458                 for (x = 0;x < CONF_COMMANDS; x++) {
459                         if (!strncasecmp(cmds[x], word, strlen(word))) {
460                                 if (++which > state) {
461                                         return strdup(cmds[x]);
462                                 }
463                         }
464                 }
465         } else if (pos == 2) {
466                 /* Conference Number */
467                 ast_mutex_lock(&conflock);
468                 cnf = confs;
469                 while(cnf) {
470                         if (!strncasecmp(word, cnf->confno, strlen(word))) {
471                                 if (++which > state)
472                                         break;
473                         }
474                         cnf = cnf->next;
475                 }
476                 ast_mutex_unlock(&conflock);
477                 return cnf ? strdup(cnf->confno) : NULL;
478         } else if (pos == 3) {
479                 /* User Number || Conf Command option*/
480                 if (strstr(line, "mute") || strstr(line, "kick")) {
481                         if ((state == 0) && (strstr(line, "kick") || strstr(line,"mute")) && !(strncasecmp(word, "all", strlen(word)))) {
482                                 return strdup("all");
483                         }
484                         which++;
485                         ast_mutex_lock(&conflock);
486                         cnf = confs;
487
488                         /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
489                         myline = ast_strdupa(line);
490                         if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
491                                 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
492                                         ;
493                         }
494                         
495                         while(cnf) {
496                                 if (strcmp(confno, cnf->confno) == 0) {
497                                         break;
498                                 }
499                                 cnf = cnf->next;
500                         }
501                         if (cnf) {
502                                 /* Search for the user */
503                                 usr = cnf->firstuser;
504                                 while(usr) {
505                                         snprintf(usrno, sizeof(usrno), "%i", usr->user_no);
506                                         if (!strncasecmp(word, usrno, strlen(word))) {
507                                                 if (++which > state)
508                                                         break;
509                                         }
510                                         usr = usr->nextuser;
511                                 }
512                         }
513                         ast_mutex_unlock(&conflock);
514                         return usr ? strdup(usrno) : NULL;
515                 }
516         }
517         return NULL;
518 }
519         
520 static char conf_usage[] =
521 "Usage: meetme  (un)lock|(un)mute|kick|list <confno> <usernumber>\n"
522 "       Executes a command for the conference or on a conferee\n";
523
524 static struct ast_cli_entry cli_conf = {
525         { "meetme", NULL, NULL }, conf_cmd,
526         "Execute a command on a conference or conferee", conf_usage, complete_confcmd };
527
528 static int confnonzero(void *ptr)
529 {
530         struct ast_conference *conf = ptr;
531         int res;
532         ast_mutex_lock(&conflock);
533         res = (conf->markedusers == 0);
534         ast_mutex_unlock(&conflock);
535         return res;
536 }
537
538 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
539 {
540         struct ast_conference *prev=NULL, *cur;
541         struct ast_conf_user *user = malloc(sizeof(struct ast_conf_user));
542         struct ast_conf_user *usr = NULL;
543         int fd;
544         struct zt_confinfo ztc;
545         struct ast_frame *f;
546         struct ast_channel *c;
547         struct ast_frame fr;
548         int outfd;
549         int ms;
550         int nfds;
551         int res;
552         int flags;
553         int retryzap;
554         int origfd;
555         int musiconhold = 0;
556         int firstpass = 0;
557         int origquiet;
558         int ret = -1;
559         int x;
560         int menu_active = 0;
561         int using_pseudo = 0;
562         int duration=20;
563         struct ast_dsp *dsp=NULL;
564
565         struct ast_app *app;
566         char *agifile;
567         char *agifiledefault = "conf-background.agi";
568         char meetmesecs[30] = "";
569         char exitcontext[AST_MAX_EXTENSION] = "";
570         char recordingtmp[AST_MAX_EXTENSION] = "";
571         int dtmf;
572
573         ZT_BUFFERINFO bi;
574         char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
575         char *buf = __buf + AST_FRIENDLY_OFFSET;
576         
577         if (!user) {
578                 ast_log(LOG_ERROR, "Out of memory\n");
579                 return(ret);
580         }
581         memset(user, 0, sizeof(struct ast_conf_user));
582
583         if (confflags & CONFFLAG_RECORDCONF && conf->recording !=MEETME_RECORD_ACTIVE) {
584                 conf->recordingfilename = pbx_builtin_getvar_helper(chan,"MEETME_RECORDINGFILE");
585                 if (!conf->recordingfilename) {
586                         snprintf(recordingtmp,sizeof(recordingtmp),"meetme-conf-rec-%s-%s",conf->confno,chan->uniqueid);
587                         conf->recordingfilename = ast_strdupa(recordingtmp);
588                 }
589                 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
590                 if (!conf->recordingformat) {
591                         snprintf(recordingtmp,sizeof(recordingtmp), "wav");
592                         conf->recordingformat = ast_strdupa(recordingtmp);
593                 }
594                 pthread_attr_init(&conf->attr);
595                 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
596                 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n", conf->confno, conf->recordingfilename, conf->recordingformat);
597                 ast_pthread_create(&conf->recordthread, &conf->attr, recordthread, conf);
598         }
599
600         user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */
601
602         time(&user->jointime);
603
604         if (conf->locked) {
605                 /* Sorry, but this confernce is locked! */      
606                 if (!ast_streamfile(chan, "conf-locked", chan->language))
607                         ast_waitstream(chan, "");
608                 goto outrun;
609         }
610         conf->users++;
611
612         if (confflags & CONFFLAG_MARKEDUSER)
613                 conf->markedusers++;
614       
615         ast_mutex_lock(&conflock);
616         if (conf->firstuser == NULL) {
617                 /* Fill the first new User struct */
618                 user->user_no = 1;
619                 user->nextuser = NULL;
620                 user->prevuser = NULL;
621                 conf->firstuser = user;
622                 conf->lastuser = user;
623         } else {
624                 /* Fill the new user struct */  
625                 user->user_no = conf->lastuser->user_no + 1; 
626                 user->prevuser = conf->lastuser;
627                 user->nextuser = NULL;
628                 if (conf->lastuser->nextuser != NULL) {
629                         ast_log(LOG_WARNING, "Error in User Management!\n");
630                         ast_mutex_unlock(&conflock);
631                         goto outrun;
632                 } else {
633                         conf->lastuser->nextuser = user;
634                         conf->lastuser = user;
635                 }
636         }
637         user->chan = chan;
638         user->userflags = confflags;
639         user->adminflags = 0;
640         user->talking = -1;
641         ast_mutex_unlock(&conflock);
642         origquiet = confflags & CONFFLAG_QUIET;
643         if (confflags & CONFFLAG_EXIT_CONTEXT) {
644                 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) 
645                         strncpy(exitcontext, agifile, sizeof(exitcontext) - 1);
646                 else if (!ast_strlen_zero(chan->macrocontext)) 
647                         strncpy(exitcontext, chan->macrocontext, sizeof(exitcontext) - 1);
648                 else
649                         strncpy(exitcontext, chan->context, sizeof(exitcontext) - 1);
650         }
651         snprintf(user->namerecloc,sizeof(user->namerecloc),"%s/meetme-username-%s-%d",AST_SPOOL_DIR,conf->confno,user->user_no);
652
653         if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER))
654                 ast_record_review(chan,"vm-rec-name",user->namerecloc, 10,"sln", &duration, NULL);
655
656         while((confflags & CONFFLAG_WAITMARKED) && (conf->markedusers == 0)) {
657                 confflags &= ~CONFFLAG_QUIET;
658                 confflags |= origquiet;
659                 /* XXX Announce that we're waiting on the conference lead to join */
660                 if (!(confflags & CONFFLAG_QUIET)) {
661                         res = ast_streamfile(chan, "vm-dialout", chan->language);
662                         if (!res)
663                                 res = ast_waitstream(chan, "");
664                 } else
665                         res = 0;
666                 /* If we're waiting with hold music, set to silent mode */
667                 if (!res) {
668                         confflags |= CONFFLAG_QUIET;
669                         ast_moh_start(chan, NULL);
670                         res = ast_safe_sleep_conditional(chan, 60000, confnonzero, conf);
671                         ast_moh_stop(chan);
672                 }
673                 if (res < 0) {
674                         ast_log(LOG_DEBUG, "Got hangup on '%s' already\n", chan->name);
675                         goto outrun;
676                 }
677         }
678         
679         if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
680                 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
681                         if (ast_waitstream(chan, "") < 0)
682                                 goto outrun;
683                 } else
684                         goto outrun;
685         }
686
687         /* Set it into linear mode (write) */
688         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
689                 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
690                 goto outrun;
691         }
692
693         /* Set it into linear mode (read) */
694         if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
695                 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
696                 goto outrun;
697         }
698         ast_indicate(chan, -1);
699         retryzap = strcasecmp(chan->type, "Zap");
700 zapretry:
701         origfd = chan->fds[0];
702         if (retryzap) {
703                 fd = open("/dev/zap/pseudo", O_RDWR);
704                 if (fd < 0) {
705                         ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
706                         goto outrun;
707                 }
708                 using_pseudo = 1;
709                 /* Make non-blocking */
710                 flags = fcntl(fd, F_GETFL);
711                 if (flags < 0) {
712                         ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
713                         close(fd);
714                         goto outrun;
715                 }
716                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
717                         ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
718                         close(fd);
719                         goto outrun;
720                 }
721                 /* Setup buffering information */
722                 memset(&bi, 0, sizeof(bi));
723                 bi.bufsize = CONF_SIZE/2;
724                 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
725                 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
726                 bi.numbufs = 4;
727                 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
728                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
729                         close(fd);
730                         goto outrun;
731                 }
732                 x = 1;
733                 if (ioctl(fd, ZT_SETLINEAR, &x)) {
734                         ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
735                         close(fd);
736                         goto outrun;
737                 }
738                 nfds = 1;
739         } else {
740                 /* XXX Make sure we're not running on a pseudo channel XXX */
741                 fd = chan->fds[0];
742                 nfds = 0;
743         }
744         memset(&ztc, 0, sizeof(ztc));
745         /* Check to see if we're in a conference... */
746         ztc.chan = 0;   
747         if (ioctl(fd, ZT_GETCONF, &ztc)) {
748                 ast_log(LOG_WARNING, "Error getting conference\n");
749                 close(fd);
750                 goto outrun;
751         }
752         if (ztc.confmode) {
753                 /* Whoa, already in a conference...  Retry... */
754                 if (!retryzap) {
755                         ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
756                         retryzap = 1;
757                         goto zapretry;
758                 }
759         }
760         memset(&ztc, 0, sizeof(ztc));
761         /* Add us to the conference */
762         ztc.chan = 0;   
763         ztc.confno = conf->zapconf;
764         ast_mutex_lock(&conflock);
765         if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER) && conf->users > 1) {
766                 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
767                         if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
768                                 ast_waitstream(conf->chan, "");
769                         if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
770                                 ast_waitstream(conf->chan, "");
771                 }
772         }
773
774         if (confflags & CONFFLAG_MONITOR)
775                 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
776         else if (confflags & CONFFLAG_TALKER)
777                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
778         else 
779                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
780
781         if (ioctl(fd, ZT_SETCONF, &ztc)) {
782                 ast_log(LOG_WARNING, "Error setting conference\n");
783                 close(fd);
784                 ast_mutex_unlock(&conflock);
785                 goto outrun;
786         }
787         ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
788
789         manager_event(EVENT_FLAG_CALL, "MeetmeJoin", 
790                         "Channel: %s\r\n"
791                         "Uniqueid: %s\r\n"
792                         "Meetme: %s\r\n"
793                         "Usernum: %i\r\n",
794                         chan->name, chan->uniqueid, conf->confno, user->user_no);
795
796         if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
797                 firstpass = 1;
798                 if (!(confflags & CONFFLAG_QUIET))
799                         conf_play(chan, conf, ENTER);
800         }
801         ast_mutex_unlock(&conflock);
802         if (confflags & CONFFLAG_AGI) {
803
804                 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
805                   or use default filename of conf-background.agi */
806
807                 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
808                 if (!agifile)
809                         agifile = agifiledefault;
810
811                 if (!strcasecmp(chan->type,"Zap")) {
812                         /*  Set CONFMUTE mode on Zap channel to mute DTMF tones */
813                         x = 1;
814                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
815                 }
816                 /* Find a pointer to the agi app and execute the script */
817                 app = pbx_findapp("agi");
818                 if (app) {
819                         ret = pbx_exec(chan, app, agifile, 1);
820                 } else {
821                         ast_log(LOG_WARNING, "Could not find application (agi)\n");
822                         ret = -2;
823                 }
824                 if (!strcasecmp(chan->type,"Zap")) {
825                         /*  Remove CONFMUTE mode on Zap channel */
826                         x = 0;
827                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
828                 }
829         } else {
830                 if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
831                         /*  Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
832                         x = 1;
833                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
834                 }       
835                 if (confflags &  CONFFLAG_MONITORTALKER && !(dsp = ast_dsp_new())) {
836                         ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
837                         res = -1;
838                 }
839                 for(;;) {
840                         outfd = -1;
841                         ms = -1;
842                         c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
843                         
844                         /* Update the struct with the actual confflags */
845                         user->userflags = confflags;
846                         
847                         /* trying to add moh for single person conf */
848                         if (confflags & CONFFLAG_MOH) {
849                                 if (conf->users == 1) {
850                                         if (musiconhold == 0) {
851                                                 ast_moh_start(chan, NULL);
852                                                 musiconhold = 1;
853                                         } 
854                                 } else {
855                                         if (musiconhold) {
856                                                 ast_moh_stop(chan);
857                                                 musiconhold = 0;
858                                         }
859                                 }
860                         }
861                         
862                         /* Leave if the last marked user left */
863                         if (conf->markedusers == 0 && confflags & CONFFLAG_MARKEDEXIT) {
864                                 ret = -1;
865                                 break;
866                         }
867         
868                         /* Check if the admin changed my modes */
869                         if (user->adminflags) {                 
870                                 /* Set the new modes */
871                                 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
872                                         ztc.confmode ^= ZT_CONF_TALKER;
873                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
874                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
875                                                 ret = -1;
876                                                 break;
877                                         }
878                                 }
879                                 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
880                                         ztc.confmode |= ZT_CONF_TALKER;
881                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
882                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
883                                                 ret = -1;
884                                                 break;
885                                         }
886                                 }
887                                 if (user->adminflags & ADMINFLAG_KICKME) {
888                                         /* You have been kicked. */
889                                         if (!ast_streamfile(chan, "conf-kicked", chan->language))
890                                                 ast_waitstream(chan, "");
891                                         ret = 0;
892                                         break;
893                                 }
894                         } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
895                                 ztc.confmode |= ZT_CONF_TALKER;
896                                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
897                                         ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
898                                         ret = -1;
899                                         break;
900                                 }
901                         }
902
903                         if (c) {
904                                 if (c->fds[0] != origfd) {
905                                         if (using_pseudo) {
906                                                 /* Kill old pseudo */
907                                                 close(fd);
908                                         }
909                                         ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
910                                         retryzap = 0;
911                                         using_pseudo = 0;
912                                         goto zapretry;
913                                 }
914                                 f = ast_read(c);
915                                 if (!f)
916                                         break;
917                                 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
918                                         if (confflags &  CONFFLAG_MONITORTALKER) {
919                                                 int totalsilence;
920                                                 if (user->talking == -1)
921                                                         user->talking = 0;
922
923                                                 res = ast_dsp_silence(dsp, f, &totalsilence);
924                                                 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
925                                                         user->talking = 1;
926                                                         manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
927                                                                 "Channel: %s\r\n"
928                                                                 "Uniqueid: %s\r\n"
929                                                                 "Meetme: %s\r\n"
930                                                                 "Usernum: %i\r\n",
931                                                                 chan->name, chan->uniqueid, conf->confno, user->user_no);
932                                                 }
933                                                 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
934                                                         user->talking = 0;
935                                                         manager_event(EVENT_FLAG_CALL, "MeetmeStopTalking",
936                                                                 "Channel: %s\r\n"
937                                                                 "Uniqueid: %s\r\n"
938                                                                 "Meetme: %s\r\n"
939                                                                 "Usernum: %i\r\n",
940                                                                 chan->name, chan->uniqueid, conf->confno, user->user_no);
941                                                 }
942                                         }
943                                         if (using_pseudo) {
944                                                 /* Carefully write */
945                                                 careful_write(fd, f->data, f->datalen);
946                                         }
947                                 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
948                                         char tmp[2];
949                                         tmp[0] = f->subclass;
950                                         tmp[1] = '\0';
951                                         if (ast_exists_extension(chan, exitcontext, tmp, 1, chan->cid.cid_num)) {
952                                                 strncpy(chan->context, exitcontext, sizeof(chan->context) - 1);
953                                                 strncpy(chan->exten, tmp, sizeof(chan->exten) - 1);
954                                                 chan->priority = 0;
955                                                 ret = 0;
956                                                 break;
957                                         }
958                                 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
959                                         ret = 0;
960                                         break;
961                                 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
962                                         if (musiconhold) {
963                                                 ast_moh_stop(chan);
964                                         }
965                                         if ((confflags & CONFFLAG_ADMIN)) {
966                                                 /* Admin menu */
967                                                 if (!menu_active) {
968                                                         menu_active = 1;
969                                                         /* Record this sound! */
970                                                          if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
971                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
972                                                         else 
973                                                                 dtmf = 0;
974                                                 } else 
975                                                         dtmf = f->subclass;
976                                                 if (dtmf) {
977                                                         switch(dtmf) {
978                                                                 case '1': /* Un/Mute */
979                                                                         menu_active = 0;
980                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
981                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
982                                                                         confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
983                                                                         } else {
984                                                                                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
985                                                                                 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
986                                                                         }
987                                                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
988                                                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
989                                                                                 ret = -1;
990                                                                                 break;
991                                                                         }
992                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
993                                                                                 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
994                                                                                         ast_waitstream(chan, "");
995                                                                         } else {
996                                                                                 if (!ast_streamfile(chan, "conf-muted", chan->language))
997                                                                                         ast_waitstream(chan, "");
998                                                                         }
999                                                                         break;
1000                                                                 case '2': /* Un/Lock the Conference */
1001                                                                         menu_active = 0;
1002                                                                         if (conf->locked) {
1003                                                                                 conf->locked = 0;
1004                                                                                 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1005                                                                                         ast_waitstream(chan, "");
1006                                                                         } else {
1007                                                                                 conf->locked = 1;
1008                                                                                 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1009                                                                                         ast_waitstream(chan, "");
1010                                                                         }
1011                                                                         break;
1012                                                                 case '3': /* Eject last user */
1013                                                                         menu_active = 0;
1014                                                                         usr = conf->lastuser;
1015                                                                         if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1016                                                                                 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1017                                                                                         ast_waitstream(chan, "");
1018                                                                         } else 
1019                                                                                 usr->adminflags |= ADMINFLAG_KICKME;
1020                                                                         ast_stopstream(chan);
1021                                                                         break;  
1022                                                                 default:
1023                                                                         menu_active = 0;
1024                                                                         /* Play an error message! */
1025                                                                         if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1026                                                                                 ast_waitstream(chan, "");
1027                                                                         break;
1028                                                         }
1029                                                 }
1030                                         } else {
1031                                                 /* User menu */
1032                                                 if (!menu_active) {
1033                                                         menu_active = 1;
1034                                                         /* Record this sound! */
1035                                                         if (!ast_streamfile(chan, "conf-usermenu", chan->language))
1036                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1037                                                         else
1038                                                                 dtmf = 0;
1039                                                 } else 
1040                                                         dtmf = f->subclass;
1041                                                 if (dtmf) {
1042                                                         switch(dtmf) {
1043                                                                 case '1': /* Un/Mute */
1044                                                                         menu_active = 0;
1045                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
1046                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1047                                                                         confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1048                                                                         } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
1049                                                                                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1050                                                                                 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1051                                                                         }
1052                                                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1053                                                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1054                                                                                 ret = -1;
1055                                                                                 break;
1056                                                                         }
1057                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
1058                                                                                 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1059                                                                                         ast_waitstream(chan, "");
1060                                                                         } else {
1061                                                                                 if (!ast_streamfile(chan, "conf-muted", chan->language))
1062                                                                                         ast_waitstream(chan, "");
1063                                                                         }
1064                                                                         break;
1065                                                                 default:
1066                                                                         menu_active = 0;
1067                                                                         /* Play an error message! */
1068                                                                         if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1069                                                                                 ast_waitstream(chan, "");
1070                                                                         break;
1071                                                         }
1072                                                 }
1073                                         }
1074                                         if (musiconhold) {
1075                                                 ast_moh_start(chan, NULL);
1076                                         }
1077                                 } else if (option_debug) {
1078                                         ast_log(LOG_DEBUG, "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",chan->name,f->frametype,f->subclass);
1079                                 }
1080                                 ast_frfree(f);
1081                         } else if (outfd > -1) {
1082                                 res = read(outfd, buf, CONF_SIZE);
1083                                 if (res > 0) {
1084                                         memset(&fr, 0, sizeof(fr));
1085                                         fr.frametype = AST_FRAME_VOICE;
1086                                         fr.subclass = AST_FORMAT_SLINEAR;
1087                                         fr.datalen = res;
1088                                         fr.samples = res/2;
1089                                         fr.data = buf;
1090                                         fr.offset = AST_FRIENDLY_OFFSET;
1091                                         if (ast_write(chan, &fr) < 0) {
1092                                                 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1093                                                 /* break; */
1094                                         }
1095                                 } else 
1096                                         ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1097                         }
1098                 }
1099         }
1100         if (using_pseudo)
1101                 close(fd);
1102         else {
1103                 /* Take out of conference */
1104                 /* Add us to the conference */
1105                 ztc.chan = 0;   
1106                 ztc.confno = 0;
1107                 ztc.confmode = 0;
1108                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1109                         ast_log(LOG_WARNING, "Error setting conference\n");
1110                 }
1111         }
1112
1113         ast_mutex_lock(&conflock);
1114         if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1115                 conf_play(chan, conf, LEAVE);
1116
1117         if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
1118                 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
1119                         if ((conf->chan) && (conf->users > 1)) {
1120                                 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1121                                         ast_waitstream(conf->chan, "");
1122                                 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1123                                         ast_waitstream(conf->chan, "");
1124                         }
1125                         ast_filedelete(user->namerecloc, NULL);
1126                 }
1127         }
1128         ast_mutex_unlock(&conflock);
1129
1130
1131 outrun:
1132         ast_mutex_lock(&conflock);
1133         if (confflags &  CONFFLAG_MONITORTALKER && dsp)
1134                 ast_dsp_free(dsp);
1135         
1136         if (user->user_no) { /* Only cleanup users who really joined! */
1137                 manager_event(EVENT_FLAG_CALL, "MeetmeLeave", 
1138                         "Channel: %s\r\n"
1139                         "Uniqueid: %s\r\n"
1140                         "Meetme: %s\r\n"
1141                         "Usernum: %i\r\n",
1142                         chan->name, chan->uniqueid, conf->confno, user->user_no);
1143                 prev = NULL;
1144                 conf->users--;
1145                 if (confflags & CONFFLAG_MARKEDUSER) 
1146                         conf->markedusers--;
1147                 cur = confs;
1148                 if (!conf->users) {
1149                         /* No more users -- close this one out */
1150                         while(cur) {
1151                                 if (cur == conf) {
1152                                         if (prev)
1153                                                 prev->next = conf->next;
1154                                         else
1155                                                 confs = conf->next;
1156                                         break;
1157                                 }
1158                                 prev = cur;
1159                                 cur = cur->next;
1160                         }
1161                         if (!cur) 
1162                                 ast_log(LOG_WARNING, "Conference not found\n");
1163                         if (conf->recording == MEETME_RECORD_ACTIVE) {
1164                                 conf->recording = MEETME_RECORD_TERMINATE;
1165                                 ast_mutex_unlock(&conflock);
1166                                 while (1) {
1167                                         ast_mutex_lock(&conflock);
1168                                         if (conf->recording == MEETME_RECORD_OFF)
1169                                                 break;
1170                                         ast_mutex_unlock(&conflock);
1171                                 }
1172                         }
1173                         if (conf->chan)
1174                                 ast_hangup(conf->chan);
1175                         else
1176                                 close(conf->fd);
1177                         free(conf);
1178                 } else {
1179                         /* Remove the user struct */ 
1180                         if (user == conf->firstuser) {
1181                                 if (user->nextuser) {
1182                                         /* There is another entry */
1183                                         user->nextuser->prevuser = NULL;
1184                                 } else {
1185                                         /* We are the only entry */
1186                                         conf->lastuser = NULL;
1187                                 }
1188                                 /* In either case */
1189                                 conf->firstuser = user->nextuser;
1190                         } else if (user == conf->lastuser){
1191                                 if (user->prevuser)
1192                                         user->prevuser->nextuser = NULL;
1193                                 else
1194                                         ast_log(LOG_ERROR, "Bad bad bad!  We're the last, not the first, but nobody before us??\n");
1195                                 conf->lastuser = user->prevuser;
1196                         } else {
1197                                 if (user->nextuser)
1198                                         user->nextuser->prevuser = user->prevuser;
1199                                 else
1200                                         ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
1201                                 if (user->prevuser)
1202                                         user->prevuser->nextuser = user->nextuser;
1203                                 else
1204                                         ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
1205                         }
1206                 }
1207                 /* Return the number of seconds the user was in the conf */
1208                 snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (time(NULL) - user->jointime));
1209                 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1210         }
1211         free(user);
1212         ast_mutex_unlock(&conflock);
1213         return ret;
1214 }
1215
1216 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
1217 {
1218         struct ast_config *cfg;
1219         struct ast_variable *var;
1220         struct ast_conference *cnf;
1221
1222         /* Check first in the conference list */
1223         ast_mutex_lock(&conflock);
1224         cnf = confs;
1225         while (cnf) {
1226                 if (!strcmp(confno, cnf->confno)) 
1227                         break;
1228                 cnf = cnf->next;
1229         }
1230         ast_mutex_unlock(&conflock);
1231
1232         if (!cnf) {
1233                 if (dynamic) {
1234                         /* No need to parse meetme.conf */
1235                         ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1236                         if (dynamic_pin) {
1237                                 if (dynamic_pin[0] == 'q') {
1238                                         /* Query the user to enter a PIN */
1239                                         ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
1240                                 }
1241                                 cnf = build_conf(confno, dynamic_pin, "", make, dynamic);
1242                         } else {
1243                                 cnf = build_conf(confno, "", "", make, dynamic);
1244                         }
1245                 } else {
1246                         /* Check the config */
1247                         cfg = ast_config_load("meetme.conf");
1248                         if (!cfg) {
1249                                 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
1250                                 return NULL;
1251                         }
1252                         var = ast_variable_browse(cfg, "rooms");
1253                         while(var) {
1254                                 if (!strcasecmp(var->name, "conf")) {
1255                                         /* Separate the PIN */
1256                                         char *pin, *pinadmin, *conf;
1257
1258                                         if ((pinadmin = ast_strdupa(var->value))) {
1259                                                 conf = strsep(&pinadmin, "|,");
1260                                                 pin = strsep(&pinadmin, "|,");
1261                                                 if (!strcasecmp(conf, confno)) {
1262                                                         /* Bingo it's a valid conference */
1263                                                         if (pin)
1264                                                                 if (pinadmin)
1265                                                                         cnf = build_conf(confno, pin, pinadmin, make, dynamic);
1266                                                                 else
1267                                                                         cnf = build_conf(confno, pin, "", make, dynamic);
1268                                                         else
1269                                                                 if (pinadmin)
1270                                                                         cnf = build_conf(confno, "", pinadmin, make, dynamic);
1271                                                                 else
1272                                                                         cnf = build_conf(confno, "", "", make, dynamic);
1273                                                         break;
1274                                                 }
1275                                         }
1276                                 }
1277                                 var = var->next;
1278                         }
1279                         if (!var) {
1280                                 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1281                         }
1282                         ast_config_destroy(cfg);
1283                 }
1284         } else if (dynamic_pin) {
1285                 /* Correct for the user selecting 'D' instead of 'd' to have
1286                    someone join into a conference that has already been created
1287                    with a pin. */
1288                 if (dynamic_pin[0] == 'q')
1289                         dynamic_pin[0] = '\0';
1290         }
1291         return cnf;
1292 }
1293
1294 /*--- count_exec: The MeetmeCount application */
1295 static int count_exec(struct ast_channel *chan, void *data)
1296 {
1297         struct localuser *u;
1298         int res = 0;
1299         struct ast_conference *conf;
1300         int count;
1301         char *confnum, *localdata;
1302         char val[80] = "0"; 
1303
1304         if (!data || ast_strlen_zero(data)) {
1305                 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1306                 return -1;
1307         }
1308         localdata = ast_strdupa(data);
1309         LOCAL_USER_ADD(u);
1310         confnum = strsep(&localdata,"|");       
1311         conf = find_conf(chan, confnum, 0, 0, NULL);
1312         if (conf)
1313                 count = conf->users;
1314         else
1315                 count = 0;
1316
1317         if (localdata && !ast_strlen_zero(localdata)){
1318                 /* have var so load it and exit */
1319                 snprintf(val,sizeof(val), "%i",count);
1320                 pbx_builtin_setvar_helper(chan, localdata,val);
1321         } else {
1322                 if (chan->_state != AST_STATE_UP)
1323                         ast_answer(chan);
1324                 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1325         }
1326         LOCAL_USER_REMOVE(u);
1327         return res;
1328 }
1329
1330 /*--- conf_exec: The meetme() application */
1331 static int conf_exec(struct ast_channel *chan, void *data)
1332 {
1333         int res=-1;
1334         struct localuser *u;
1335         char confno[AST_MAX_EXTENSION] = "";
1336         int allowretry = 0;
1337         int retrycnt = 0;
1338         struct ast_conference *cnf;
1339         int confflags = 0;
1340         int dynamic = 0;
1341         int empty = 0, empty_no_pin = 0;
1342         int always_prompt = 0;
1343         char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
1344
1345         if (!data || ast_strlen_zero(data)) {
1346                 allowretry = 1;
1347                 notdata = "";
1348         } else {
1349                 notdata = data;
1350         }
1351         LOCAL_USER_ADD(u);
1352         if (chan->_state != AST_STATE_UP)
1353                 ast_answer(chan);
1354
1355         info = ast_strdupa((char *)notdata);
1356
1357         if (info) {
1358                 char *tmp = strsep(&info, "|");
1359                 strncpy(confno, tmp, sizeof(confno) - 1);
1360                 if (ast_strlen_zero(confno)) {
1361                         allowretry = 1;
1362                 }
1363         }
1364         if (info)
1365                 inflags = strsep(&info, "|");
1366         if (info)
1367                 inpin = strsep(&info, "|");
1368         if (inpin)
1369                 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
1370
1371         if (inflags) {
1372                 if (strchr(inflags, 'a'))
1373                         confflags |= CONFFLAG_ADMIN;
1374                 if (strchr(inflags, 'T'))
1375                         confflags |= CONFFLAG_MONITORTALKER;
1376                 if (strchr(inflags, 'i'))
1377                         confflags |= CONFFLAG_INTROUSER;
1378                 if (strchr(inflags, 'm'))
1379                         confflags |= CONFFLAG_MONITOR;
1380                 if (strchr(inflags, 'p'))
1381                         confflags |= CONFFLAG_POUNDEXIT;
1382                 if (strchr(inflags, 's'))
1383                         confflags |= CONFFLAG_STARMENU;
1384                 if (strchr(inflags, 't'))
1385                         confflags |= CONFFLAG_TALKER;
1386                 if (strchr(inflags, 'q'))
1387                         confflags |= CONFFLAG_QUIET;
1388                 if (strchr(inflags, 'M'))
1389                         confflags |= CONFFLAG_MOH;
1390                 if (strchr(inflags, 'x'))
1391                         confflags |= CONFFLAG_MARKEDEXIT;
1392                 if (strchr(inflags, 'X'))
1393                         confflags |= CONFFLAG_EXIT_CONTEXT;
1394                 if (strchr(inflags, 'A'))
1395                         confflags |= CONFFLAG_MARKEDUSER;
1396                 if (strchr(inflags, 'b'))
1397                         confflags |= CONFFLAG_AGI;
1398                 if (strchr(inflags, 'w'))
1399                         confflags |= CONFFLAG_WAITMARKED;
1400                 if (strchr(inflags, 'r'))
1401                         confflags |= CONFFLAG_RECORDCONF;       
1402                 if (strchr(inflags, 'd'))
1403                         dynamic = 1;
1404                 if (strchr(inflags, 'D')) {
1405                         dynamic = 1;
1406                         if (! inpin) {
1407                                 strncpy(the_pin, "q", sizeof(the_pin) - 1);
1408                         }
1409                 }
1410                 if (strchr(inflags, 'e'))
1411                         empty = 1;
1412                 if (strchr(inflags, 'E')) {
1413                         empty = 1;
1414                         empty_no_pin = 1;
1415                 }
1416                 if (strchr(inflags, 'P'))
1417                         always_prompt = 1;
1418         }
1419
1420         do {
1421                 if (retrycnt > 3)
1422                         allowretry = 0;
1423                 if (empty) {
1424                         int i, map[1024];
1425                         struct ast_config *cfg;
1426                         struct ast_variable *var;
1427                         int confno_int;
1428
1429                         memset(map, 0, sizeof(map));
1430
1431                         ast_mutex_lock(&conflock);
1432                         cnf = confs;
1433                         while (cnf) {
1434                                 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
1435                                         /* Disqualify in use conference */
1436                                         if (confno_int >= 0 && confno_int < 1024)
1437                                                 map[confno_int]++;
1438                                 }
1439                                 cnf = cnf->next;
1440                         }
1441                         ast_mutex_unlock(&conflock);
1442
1443                         /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
1444                         if ((empty_no_pin) || (!dynamic)) {
1445                                 cfg = ast_config_load("meetme.conf");
1446                                 if (cfg) {
1447                                         var = ast_variable_browse(cfg, "rooms");
1448                                         while(var) {
1449                                                 if (!strcasecmp(var->name, "conf")) {
1450                                                         char *stringp = ast_strdupa(var->value);
1451                                                         if (stringp) {
1452                                                                 char *confno_tmp = strsep(&stringp, "|,");
1453                                                                 int found = 0;
1454                                                                 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
1455                                                                         if ((confno_int >= 0) && (confno_int < 1024)) {
1456                                                                                 if (stringp && empty_no_pin) {
1457                                                                                         map[confno_int]++;
1458                                                                                 }
1459                                                                         }
1460                                                                 }
1461                                                                 if (! dynamic) {
1462                                                                         /* For static:  run through the list and see if this conference is empty */
1463                                                                         ast_mutex_lock(&conflock);
1464                                                                         cnf = confs;
1465                                                                         while (cnf) {
1466                                                                                 if (!strcmp(confno_tmp, cnf->confno)) {
1467                                                                                         /* The conference exists, therefore it's not empty */
1468                                                                                         found = 1;
1469                                                                                         break;
1470                                                                                 }
1471                                                                                 cnf = cnf->next;
1472                                                                         }
1473                                                                         ast_mutex_unlock(&conflock);
1474                                                                         if (!found) {
1475                                                                                 /* At this point, we have a confno_tmp (static conference) that is empty */
1476                                                                                 if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) {
1477                                                                                 /* Case 1:  empty_no_pin and pin is nonexistant (NULL)
1478                                                                                  * Case 2:  empty_no_pin and pin is blank (but not NULL)
1479                                                                                  * Case 3:  not empty_no_pin
1480                                                                                  */
1481                                                                                         strncpy(confno, confno_tmp, sizeof(confno) - 1);
1482                                                                                         break;
1483                                                                                         /* XXX the map is not complete (but we do have a confno) */
1484                                                                                 }
1485                                                                         }
1486                                                                 }
1487                                                         } else {
1488                                                                 ast_log(LOG_ERROR, "Out of memory\n");
1489                                                         }
1490                                                 }
1491                                                 var = var->next;
1492                                         }
1493                                         ast_config_destroy(cfg);
1494                                 }
1495                         }
1496                         /* Select first conference number not in use */
1497                         if (ast_strlen_zero(confno) && dynamic) {
1498                                 for (i=0;i<1024;i++) {
1499                                         if (!map[i]) {
1500                                                 snprintf(confno, sizeof(confno), "%d", i);
1501                                                 break;
1502                                         }
1503                                 }
1504                         }
1505
1506                         /* Not found? */
1507                         if (ast_strlen_zero(confno)) {
1508                                 res = ast_streamfile(chan, "conf-noempty", chan->language);
1509                                 if (!res)
1510                                         ast_waitstream(chan, "");
1511                         } else {
1512                                 if (sscanf(confno, "%d", &confno_int) == 1) {
1513                                         res = ast_streamfile(chan, "conf-enteringno", chan->language);
1514                                         if (!res) {
1515                                                 ast_waitstream(chan, "");
1516                                                 res = ast_say_digits(chan, confno_int, "", chan->language);
1517                                         }
1518                                 } else {
1519                                         ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
1520                                 }
1521                         }
1522                 }
1523                 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
1524                         /* Prompt user for conference number */
1525                         res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
1526                         if (res < 0) {
1527                                 /* Don't try to validate when we catch an error */
1528                                 confno[0] = '\0';
1529                                 allowretry = 0;
1530                                 break;
1531                         }
1532                 }
1533                 if (!ast_strlen_zero(confno)) {
1534                         /* Check the validity of the conference */
1535                         cnf = find_conf(chan, confno, 1, dynamic, the_pin);
1536                         if (!cnf) {
1537                                 res = ast_streamfile(chan, "conf-invalid", chan->language);
1538                                 if (!res)
1539                                         ast_waitstream(chan, "");
1540                                 res = -1;
1541                                 if (allowretry)
1542                                         confno[0] = '\0';
1543                         } else {
1544                                 if ((!ast_strlen_zero(cnf->pin) && ! (confflags & CONFFLAG_ADMIN)) || (!ast_strlen_zero(cnf->pinadmin) && (confflags & CONFFLAG_ADMIN))) {
1545                                         char pin[AST_MAX_EXTENSION]="";
1546                                         int j;
1547
1548                                         /* Allow the pin to be retried up to 3 times */
1549                                         for (j=0; j<3; j++) {
1550                                                 if (*the_pin && (always_prompt==0)) {
1551                                                         strncpy(pin, the_pin, sizeof(pin) - 1);
1552                                                         res = 0;
1553                                                 } else {
1554                                                         /* Prompt user for pin if pin is required */
1555                                                         res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
1556                                                 }
1557                                                 if (res >= 0) {
1558                                                         if (!strcasecmp(pin, cnf->pin)  || (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))) {
1559
1560                                                                 /* Pin correct */
1561                                                                 allowretry = 0;
1562                                                                 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) 
1563                                                                         confflags |= CONFFLAG_ADMIN;
1564                                                                 /* Run the conference */
1565                                                                 res = conf_run(chan, cnf, confflags);
1566                                                                 break;
1567                                                         } else {
1568                                                                 /* Pin invalid */
1569                                                                 res = ast_streamfile(chan, "conf-invalidpin", chan->language);
1570                                                                 if (!res)
1571                                                                         ast_waitstream(chan, AST_DIGIT_ANY);
1572                                                                 if (res < 0)
1573                                                                         break;
1574                                                                 pin[0] = res;
1575                                                                 pin[1] = '\0';
1576                                                                 res = -1;
1577                                                                 if (allowretry)
1578                                                                         confno[0] = '\0';
1579                                                         }
1580                                                 } else {
1581                                                         res = -1;
1582                                                         allowretry = 0;
1583                                                         break;
1584                                                 }
1585
1586                                                 /* Don't retry pin with a static pin */
1587                                                 if (*the_pin && (always_prompt==0)) {
1588                                                         break;
1589                                                 }
1590                                         }
1591                                 } else {
1592                                         /* No pin required */
1593                                         allowretry = 0;
1594
1595                                         /* Run the conference */
1596                                         res = conf_run(chan, cnf, confflags);
1597                                 }
1598                         }
1599                 }
1600         } while (allowretry);
1601         /* Do the conference */
1602         LOCAL_USER_REMOVE(u);
1603         return res;
1604 }
1605
1606 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
1607         struct ast_conf_user *user = NULL;
1608         char usrno[1024] = "";
1609         if (conf && callerident) {
1610                 user = conf->firstuser;
1611                 while(user) {
1612                         snprintf(usrno, sizeof(usrno), "%i", user->user_no);
1613                         if (strcmp(usrno, callerident) == 0)
1614                                 return user;
1615                         user = user->nextuser;
1616                 }
1617         }
1618         return NULL;
1619 }
1620
1621 /*--- admin_exec: The MeetMeadmin application */
1622 /* MeetMeAdmin(confno, command, caller) */
1623 static int admin_exec(struct ast_channel *chan, void *data) {
1624         char *params, *command = NULL, *caller = NULL, *conf = NULL;
1625         struct ast_conference *cnf;
1626         struct ast_conf_user *user = NULL;
1627
1628         ast_mutex_lock(&conflock);
1629         /* The param has the conference number the user and the command to execute */
1630         if (data && !ast_strlen_zero(data)) {           
1631                 params = ast_strdupa((char *) data);
1632                 conf = strsep(&params, "|");
1633                 command = strsep(&params, "|");
1634                 caller = strsep(&params, "|");
1635                 
1636                 if (!command) {
1637                         ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
1638                         ast_mutex_unlock(&conflock);
1639                         return -1;
1640                 }
1641                 cnf = confs;
1642                 while (cnf) {
1643                         if (strcmp(cnf->confno, conf) == 0) 
1644                                 break;
1645                         cnf = cnf->next;
1646                 }
1647                 
1648                 if (caller)
1649                         user = find_user(cnf, caller);
1650                 
1651                 if (cnf) {
1652                         switch((int) (*command)) {
1653                                 case 76: /* L: Lock */ 
1654                                         cnf->locked = 1;
1655                                         break;
1656                                 case 108: /* l: Unlock */ 
1657                                         cnf->locked = 0;
1658                                         break;
1659                                 case 75: /* K: kick all users*/
1660                                         user = cnf->firstuser;
1661                                         while(user) {
1662                                                 user->adminflags |= ADMINFLAG_KICKME;
1663                                                 if (user->nextuser) {
1664                                                         user = user->nextuser;
1665                                                 } else {
1666                                                         break;
1667                                                 }
1668                                         }
1669                                         break;
1670                                 case 101: /* e: Eject last user*/
1671                                         user = cnf->lastuser;
1672                                         if (!(user->userflags & CONFFLAG_ADMIN)) {
1673                                                 user->adminflags |= ADMINFLAG_KICKME;
1674                                                 break;
1675                                         } else
1676                                                 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
1677                                         break;
1678                                 case 77: /* M: Mute */ 
1679                                         if (user) {
1680                                                 user->adminflags |= ADMINFLAG_MUTED;
1681                                         } else {
1682                                                 ast_log(LOG_NOTICE, "Specified User not found!\n");
1683                                         }
1684                                         break;
1685                                 case 78: /* N: Mute all users */
1686                                         user = cnf->firstuser;
1687                                         while(user) {
1688                                                 if (user && !(user->userflags & CONFFLAG_ADMIN))
1689                                                         user->adminflags |= ADMINFLAG_MUTED;
1690                                                 if (user->nextuser) {
1691                                                         user = user->nextuser;
1692                                                 } else {
1693                                                         break;
1694                                                 }
1695                                         }
1696                                         break;                                  
1697                                 case 109: /* m: Unmute */ 
1698                                         if (user && (user->adminflags & ADMINFLAG_MUTED)) {
1699                                                 user->adminflags ^= ADMINFLAG_MUTED;
1700                                         } else {
1701                                                 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
1702                                         }
1703                                         break;
1704                                 case  110: /* n: Unmute all users */
1705                                         user = cnf->firstuser;
1706                                         while(user) {
1707                                                 if (user && (user-> adminflags & ADMINFLAG_MUTED)) {
1708                                                         user->adminflags ^= ADMINFLAG_MUTED;
1709                                                 }
1710                                                 if (user->nextuser) {
1711                                                         user = user->nextuser;
1712                                                 } else {
1713                                                         break;
1714                                                 }
1715                                         }
1716                                         break;
1717                                 case 107: /* k: Kick user */ 
1718                                         if (user) {
1719                                                 user->adminflags |= ADMINFLAG_KICKME;
1720                                         } else {
1721                                                 ast_log(LOG_NOTICE, "Specified User not found!");
1722                                         }
1723                                         break;
1724                         }
1725                 } else {
1726                         ast_log(LOG_NOTICE, "Conference Number not found\n");
1727                 }
1728         }
1729         ast_mutex_unlock(&conflock);
1730         return 0;
1731 }
1732
1733 static void *recordthread(void *args)
1734 {
1735         struct ast_conference *cnf;
1736         struct ast_frame *f=NULL;
1737         int flags;
1738         struct ast_filestream *s;
1739         int res=0;
1740
1741         cnf = (struct ast_conference *)args;
1742         if( !cnf || !cnf->chan ) {
1743                 pthread_exit(0);
1744         }
1745         ast_stopstream(cnf->chan);
1746         flags = O_CREAT|O_TRUNC|O_WRONLY;
1747         s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
1748
1749         if (s) {
1750                 cnf->recording = MEETME_RECORD_ACTIVE;
1751                 while (ast_waitfor(cnf->chan, -1) > -1) {
1752                         f = ast_read(cnf->chan);
1753                         if (!f) {
1754                                 res = -1;
1755                                 break;
1756                         }
1757                         if (f->frametype == AST_FRAME_VOICE) {
1758                                 res = ast_writestream(s, f);
1759                                 if (res) 
1760                                         break;
1761                         }
1762                         ast_frfree(f);
1763                         if (cnf->recording == MEETME_RECORD_TERMINATE) {
1764                                 ast_mutex_lock(&conflock);
1765                                 ast_mutex_unlock(&conflock);
1766                                 break;
1767                         }
1768                 }
1769                 cnf->recording = MEETME_RECORD_OFF;
1770                 ast_closestream(s);
1771         }
1772         pthread_exit(0);
1773 }
1774
1775 int unload_module(void)
1776 {
1777         STANDARD_HANGUP_LOCALUSERS;
1778         ast_cli_unregister(&cli_show_confs);
1779         ast_cli_unregister(&cli_conf);
1780         ast_unregister_application(app3);
1781         ast_unregister_application(app2);
1782         return ast_unregister_application(app);
1783 }
1784
1785 int load_module(void)
1786 {
1787         ast_cli_register(&cli_show_confs);
1788         ast_cli_register(&cli_conf);
1789         ast_register_application(app3, admin_exec, synopsis3, descrip3);
1790         ast_register_application(app2, count_exec, synopsis2, descrip2);
1791         return ast_register_application(app, conf_exec, synopsis, descrip);
1792 }
1793
1794
1795 char *description(void)
1796 {
1797         return tdesc;
1798 }
1799
1800 int usecount(void)
1801 {
1802         int res;
1803         STANDARD_USECOUNT(res);
1804         return res;
1805 }
1806
1807 char *key()
1808 {
1809         return ASTERISK_GPL_KEY;
1810 }
1811