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