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