Fix potential segfault (bug #3479)
[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                                 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
936                                         char tmp[2];
937                                         tmp[0] = f->subclass;
938                                         tmp[1] = '\0';
939                                         if (ast_exists_extension(chan, exitcontext, tmp, 1, chan->cid.cid_num)) {
940                                                 strncpy(chan->context, exitcontext, sizeof(chan->context) - 1);
941                                                 strncpy(chan->exten, tmp, sizeof(chan->exten) - 1);
942                                                 chan->priority = 0;
943                                                 ret = 0;
944                                                 break;
945                                         }
946                                 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
947                                         ret = 0;
948                                         break;
949                                 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
950                                         if (musiconhold) {
951                                                 ast_moh_stop(chan);
952                                         }
953                                         if ((confflags & CONFFLAG_ADMIN)) {
954                                                 /* Admin menu */
955                                                 if (!menu_active) {
956                                                         menu_active = 1;
957                                                         /* Record this sound! */
958                                                          if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
959                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
960                                                         else 
961                                                                 dtmf = 0;
962                                                 } else 
963                                                         dtmf = f->subclass;
964                                                 if (dtmf) {
965                                                         switch(dtmf) {
966                                                                 case '1': /* Un/Mute */
967                                                                         menu_active = 0;
968                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
969                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
970                                                                         confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
971                                                                         } else {
972                                                                                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
973                                                                                 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
974                                                                         }
975                                                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
976                                                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
977                                                                                 ret = -1;
978                                                                                 break;
979                                                                         }
980                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
981                                                                                 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
982                                                                                         ast_waitstream(chan, "");
983                                                                         } else {
984                                                                                 if (!ast_streamfile(chan, "conf-muted", chan->language))
985                                                                                         ast_waitstream(chan, "");
986                                                                         }
987                                                                         break;
988                                                                 case '2': /* Un/Lock the Conference */
989                                                                         menu_active = 0;
990                                                                         if (conf->locked) {
991                                                                                 conf->locked = 0;
992                                                                                 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
993                                                                                         ast_waitstream(chan, "");
994                                                                         } else {
995                                                                                 conf->locked = 1;
996                                                                                 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
997                                                                                         ast_waitstream(chan, "");
998                                                                         }
999                                                                         break;
1000                                                                 case '3': /* Eject last user */
1001                                                                         menu_active = 0;
1002                                                                         usr = conf->lastuser;
1003                                                                         if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1004                                                                                 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1005                                                                                         ast_waitstream(chan, "");
1006                                                                         } else 
1007                                                                                 usr->adminflags |= ADMINFLAG_KICKME;
1008                                                                         ast_stopstream(chan);
1009                                                                         break;  
1010                                                                 default:
1011                                                                         menu_active = 0;
1012                                                                         /* Play an error message! */
1013                                                                         if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1014                                                                                 ast_waitstream(chan, "");
1015                                                                         break;
1016                                                         }
1017                                                 }
1018                                         } else {
1019                                                 /* User menu */
1020                                                 if (!menu_active) {
1021                                                         menu_active = 1;
1022                                                         /* Record this sound! */
1023                                                         if (!ast_streamfile(chan, "conf-usermenu", chan->language))
1024                                                                 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1025                                                         else
1026                                                                 dtmf = 0;
1027                                                 } else 
1028                                                         dtmf = f->subclass;
1029                                                 if (dtmf) {
1030                                                         switch(dtmf) {
1031                                                                 case '1': /* Un/Mute */
1032                                                                         menu_active = 0;
1033                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
1034                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1035                                                                         confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1036                                                                         } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
1037                                                                                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1038                                                                                 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1039                                                                         }
1040                                                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
1041                                                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1042                                                                                 ret = -1;
1043                                                                                 break;
1044                                                                         }
1045                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
1046                                                                                 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1047                                                                                         ast_waitstream(chan, "");
1048                                                                         } else {
1049                                                                                 if (!ast_streamfile(chan, "conf-muted", chan->language))
1050                                                                                         ast_waitstream(chan, "");
1051                                                                         }
1052                                                                         break;
1053                                                                 default:
1054                                                                         menu_active = 0;
1055                                                                         /* Play an error message! */
1056                                                                         if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1057                                                                                 ast_waitstream(chan, "");
1058                                                                         break;
1059                                                         }
1060                                                 }
1061                                         }
1062                                         if (musiconhold) {
1063                                                 ast_moh_start(chan, NULL);
1064                                         }
1065                                 } else if (using_pseudo) {
1066                                         if (f->frametype == AST_FRAME_VOICE) {
1067                                                 if (f->subclass == AST_FORMAT_SLINEAR) {
1068                                                         /* Carefully write */
1069                                                         careful_write(fd, f->data, f->datalen);
1070                                                 } else
1071                                                         ast_log(LOG_WARNING, "Huh?  Got a non-linear (%d) frame in the conference\n", f->subclass);
1072                                         }
1073                                 }
1074                                 ast_frfree(f);
1075                         } else if (outfd > -1) {
1076                                 res = read(outfd, buf, CONF_SIZE);
1077                                 if (res > 0) {
1078                                         memset(&fr, 0, sizeof(fr));
1079                                         fr.frametype = AST_FRAME_VOICE;
1080                                         fr.subclass = AST_FORMAT_SLINEAR;
1081                                         fr.datalen = res;
1082                                         fr.samples = res/2;
1083                                         fr.data = buf;
1084                                         fr.offset = AST_FRIENDLY_OFFSET;
1085                                         if (ast_write(chan, &fr) < 0) {
1086                                                 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1087                                                 /* break; */
1088                                         }
1089                                 } else 
1090                                         ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1091                         }
1092                 }
1093         }
1094         if (using_pseudo)
1095                 close(fd);
1096         else {
1097                 /* Take out of conference */
1098                 /* Add us to the conference */
1099                 ztc.chan = 0;   
1100                 ztc.confno = 0;
1101                 ztc.confmode = 0;
1102                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1103                         ast_log(LOG_WARNING, "Error setting conference\n");
1104                 }
1105         }
1106
1107         ast_mutex_lock(&conflock);
1108         if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1109                 conf_play(conf, LEAVE);
1110
1111         if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER) && conf->users > 1) {
1112                 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1113                         if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1114                                 ast_waitstream(conf->chan, "");
1115                         if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1116                                 ast_waitstream(conf->chan, "");
1117                         ast_filedelete(user->namerecloc, NULL);
1118
1119                 }
1120         }
1121         ast_mutex_unlock(&conflock);
1122
1123
1124 outrun:
1125         ast_mutex_lock(&conflock);
1126         if (confflags &  CONFFLAG_MONITORTALKER && dsp)
1127                 ast_dsp_free(dsp);
1128         
1129         if (user->user_no) { /* Only cleanup users who really joined! */
1130                 manager_event(EVENT_FLAG_CALL, "MeetmeLeave", 
1131                         "Channel: %s\r\n"
1132                         "Uniqueid: %s\r\n"
1133                         "Meetme: %s\r\n"
1134                         "Usernum: %i\r\n",
1135                         chan->name, chan->uniqueid, conf->confno, user->user_no);
1136                 prev = NULL;
1137                 conf->users--;
1138                 if (confflags & CONFFLAG_MARKEDUSER) 
1139                         conf->markedusers--;
1140                 cur = confs;
1141                 if (!conf->users) {
1142                         /* No more users -- close this one out */
1143                         while(cur) {
1144                                 if (cur == conf) {
1145                                         if (prev)
1146                                                 prev->next = conf->next;
1147                                         else
1148                                                 confs = conf->next;
1149                                         break;
1150                                 }
1151                                 prev = cur;
1152                                 cur = cur->next;
1153                         }
1154                         if (!cur) 
1155                                 ast_log(LOG_WARNING, "Conference not found\n");
1156                         if (conf->recording == MEETME_RECORD_ACTIVE) {
1157                                 conf->recording = MEETME_RECORD_TERMINATE;
1158                                 ast_mutex_unlock(&conflock);
1159                                 while (1) {
1160                                         ast_mutex_lock(&conflock);
1161                                         if (conf->recording == MEETME_RECORD_OFF)
1162                                                 break;
1163                                         ast_mutex_unlock(&conflock);
1164                                 }
1165                         }
1166                         if (conf->chan)
1167                                 ast_hangup(conf->chan);
1168                         else
1169                                 close(conf->fd);
1170                         free(conf);
1171                 } else {
1172                         /* Remove the user struct */ 
1173                         if (user == conf->firstuser) {
1174                                 if (user->nextuser) {
1175                                         /* There is another entry */
1176                                         user->nextuser->prevuser = NULL;
1177                                 } else {
1178                                         /* We are the only entry */
1179                                         conf->lastuser = NULL;
1180                                 }
1181                                 /* In either case */
1182                                 conf->firstuser = user->nextuser;
1183                         } else if (user == conf->lastuser){
1184                                 if (user->prevuser)
1185                                         user->prevuser->nextuser = NULL;
1186                                 else
1187                                         ast_log(LOG_ERROR, "Bad bad bad!  We're the last, not the first, but nobody before us??\n");
1188                                 conf->lastuser = user->prevuser;
1189                         } else {
1190                                 if (user->nextuser)
1191                                         user->nextuser->prevuser = user->prevuser;
1192                                 else
1193                                         ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
1194                                 if (user->prevuser)
1195                                         user->prevuser->nextuser = user->nextuser;
1196                                 else
1197                                         ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
1198                         }
1199                 }
1200                 /* Return the number of seconds the user was in the conf */
1201                 snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (time(NULL) - user->jointime));
1202                 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1203         }
1204         free(user);
1205         ast_mutex_unlock(&conflock);
1206         return ret;
1207 }
1208
1209 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
1210 {
1211         struct ast_config *cfg;
1212         struct ast_variable *var;
1213         struct ast_conference *cnf;
1214
1215         /* Check first in the conference list */
1216         ast_mutex_lock(&conflock);
1217         cnf = confs;
1218         while (cnf) {
1219                 if (!strcmp(confno, cnf->confno)) 
1220                         break;
1221                 cnf = cnf->next;
1222         }
1223         ast_mutex_unlock(&conflock);
1224
1225         if (!cnf) {
1226                 if (dynamic) {
1227                         /* No need to parse meetme.conf */
1228                         ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1229                         if (dynamic_pin) {
1230                                 if (dynamic_pin[0] == 'q') {
1231                                         /* Query the user to enter a PIN */
1232                                         ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
1233                                 }
1234                                 cnf = build_conf(confno, dynamic_pin, make, dynamic);
1235                         } else {
1236                                 cnf = build_conf(confno, "", make, dynamic);
1237                         }
1238                 } else {
1239                         /* Check the config */
1240                         cfg = ast_config_load("meetme.conf");
1241                         if (!cfg) {
1242                                 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
1243                                 return NULL;
1244                         }
1245                         var = ast_variable_browse(cfg, "rooms");
1246                         while(var) {
1247                                 if (!strcasecmp(var->name, "conf")) {
1248                                         /* Separate the PIN */
1249                                         char *pin, *conf;
1250
1251                                         if ((pin = ast_strdupa(var->value))) {
1252                                                 conf = strsep(&pin, "|,");
1253                                                 if (!strcasecmp(conf, confno)) {
1254                                                         /* Bingo it's a valid conference */
1255                                                         if (pin)
1256                                                                 cnf = build_conf(confno, pin, make, dynamic);
1257                                                         else
1258                                                                 cnf = build_conf(confno, "", make, dynamic);
1259                                                         break;
1260                                                 }
1261                                         }
1262                                 }
1263                                 var = var->next;
1264                         }
1265                         if (!var) {
1266                                 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1267                         }
1268                         ast_config_destroy(cfg);
1269                 }
1270         } else if (dynamic_pin) {
1271                 /* Correct for the user selecting 'D' instead of 'd' to have
1272                    someone join into a conference that has already been created
1273                    with a pin. */
1274                 if (dynamic_pin[0] == 'q')
1275                         dynamic_pin[0] = '\0';
1276         }
1277         return cnf;
1278 }
1279
1280 /*--- count_exec: The MeetmeCount application */
1281 static int count_exec(struct ast_channel *chan, void *data)
1282 {
1283         struct localuser *u;
1284         int res = 0;
1285         struct ast_conference *conf;
1286         int count;
1287         char *confnum, *localdata;
1288         char val[80] = "0"; 
1289
1290         if (!data || ast_strlen_zero(data)) {
1291                 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1292                 return -1;
1293         }
1294         localdata = ast_strdupa(data);
1295         LOCAL_USER_ADD(u);
1296         confnum = strsep(&localdata,"|");       
1297         conf = find_conf(chan, confnum, 0, 0, NULL);
1298         if (conf)
1299                 count = conf->users;
1300         else
1301                 count = 0;
1302
1303         if (localdata && !ast_strlen_zero(localdata)){
1304                 /* have var so load it and exit */
1305                 snprintf(val,sizeof(val), "%i",count);
1306                 pbx_builtin_setvar_helper(chan, localdata,val);
1307         } else {
1308                 if (chan->_state != AST_STATE_UP)
1309                         ast_answer(chan);
1310                 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1311         }
1312         LOCAL_USER_REMOVE(u);
1313         return res;
1314 }
1315
1316 /*--- conf_exec: The meetme() application */
1317 static int conf_exec(struct ast_channel *chan, void *data)
1318 {
1319         int res=-1;
1320         struct localuser *u;
1321         char confno[AST_MAX_EXTENSION] = "";
1322         int allowretry = 0;
1323         int retrycnt = 0;
1324         struct ast_conference *cnf;
1325         int confflags = 0;
1326         int dynamic = 0;
1327         int empty = 0, empty_no_pin = 0;
1328         char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
1329
1330         if (!data || ast_strlen_zero(data)) {
1331                 allowretry = 1;
1332                 notdata = "";
1333         } else {
1334                 notdata = data;
1335         }
1336         LOCAL_USER_ADD(u);
1337         if (chan->_state != AST_STATE_UP)
1338                 ast_answer(chan);
1339
1340         info = ast_strdupa((char *)notdata);
1341
1342         if (info) {
1343                 char *tmp = strsep(&info, "|");
1344                 strncpy(confno, tmp, sizeof(confno) - 1);
1345                 if (ast_strlen_zero(confno)) {
1346                         allowretry = 1;
1347                 }
1348         }
1349         if (info)
1350                 inflags = strsep(&info, "|");
1351         if (info)
1352                 inpin = strsep(&info, "|");
1353         if (inpin)
1354                 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
1355
1356         if (inflags) {
1357                 if (strchr(inflags, 'a'))
1358                         confflags |= CONFFLAG_ADMIN;
1359                 if (strchr(inflags, 'T'))
1360                         confflags |= CONFFLAG_MONITORTALKER;
1361                 if (strchr(inflags, 'i'))
1362                         confflags |= CONFFLAG_INTROUSER;
1363                 if (strchr(inflags, 'm'))
1364                         confflags |= CONFFLAG_MONITOR;
1365                 if (strchr(inflags, 'p'))
1366                         confflags |= CONFFLAG_POUNDEXIT;
1367                 if (strchr(inflags, 's'))
1368                         confflags |= CONFFLAG_STARMENU;
1369                 if (strchr(inflags, 't'))
1370                         confflags |= CONFFLAG_TALKER;
1371                 if (strchr(inflags, 'q'))
1372                         confflags |= CONFFLAG_QUIET;
1373                 if (strchr(inflags, 'M'))
1374                         confflags |= CONFFLAG_MOH;
1375                 if (strchr(inflags, 'x'))
1376                         confflags |= CONFFLAG_MARKEDEXIT;
1377                 if (strchr(inflags, 'X'))
1378                         confflags |= CONFFLAG_EXIT_CONTEXT;
1379                 if (strchr(inflags, 'A'))
1380                         confflags |= CONFFLAG_MARKEDUSER;
1381                 if (strchr(inflags, 'b'))
1382                         confflags |= CONFFLAG_AGI;
1383                 if (strchr(inflags, 'w'))
1384                         confflags |= CONFFLAG_WAITMARKED;
1385                 if (strchr(inflags, 'r'))
1386                         confflags |= CONFFLAG_RECORDCONF;       
1387                 if (strchr(inflags, 'd'))
1388                         dynamic = 1;
1389                 if (strchr(inflags, 'D')) {
1390                         dynamic = 1;
1391                         if (! inpin) {
1392                                 strncpy(the_pin, "q", sizeof(the_pin) - 1);
1393                         }
1394                 }
1395                 if (strchr(inflags, 'e'))
1396                         empty = 1;
1397                 if (strchr(inflags, 'E')) {
1398                         empty = 1;
1399                         empty_no_pin = 1;
1400                 }
1401         }
1402
1403         do {
1404                 if (retrycnt > 3)
1405                         allowretry = 0;
1406                 if (empty) {
1407                         int i, map[1024];
1408                         struct ast_config *cfg;
1409                         struct ast_variable *var;
1410                         int confno_int;
1411
1412                         memset(map, 0, sizeof(map));
1413
1414                         ast_mutex_lock(&conflock);
1415                         cnf = confs;
1416                         while (cnf) {
1417                                 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
1418                                         /* Disqualify in use conference */
1419                                         if (confno_int >= 0 && confno_int < 1024)
1420                                                 map[confno_int]++;
1421                                 }
1422                                 cnf = cnf->next;
1423                         }
1424                         ast_mutex_unlock(&conflock);
1425
1426                         /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
1427                         if ((empty_no_pin) || (!dynamic)) {
1428                                 cfg = ast_config_load("meetme.conf");
1429                                 if (cfg) {
1430                                         var = ast_variable_browse(cfg, "rooms");
1431                                         while(var) {
1432                                                 if (!strcasecmp(var->name, "conf")) {
1433                                                         char *stringp = ast_strdupa(var->value);
1434                                                         if (stringp) {
1435                                                                 char *confno_tmp = strsep(&stringp, "|,");
1436                                                                 int found = 0;
1437                                                                 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
1438                                                                         if ((confno_int >= 0) && (confno_int < 1024)) {
1439                                                                                 if (stringp && empty_no_pin) {
1440                                                                                         map[confno_int]++;
1441                                                                                 }
1442                                                                         }
1443                                                                 }
1444                                                                 if (! dynamic) {
1445                                                                         /* For static:  run through the list and see if this conference is empty */
1446                                                                         ast_mutex_lock(&conflock);
1447                                                                         cnf = confs;
1448                                                                         while (cnf) {
1449                                                                                 if (!strcmp(confno_tmp, cnf->confno)) {
1450                                                                                         /* The conference exists, therefore it's not empty */
1451                                                                                         found = 1;
1452                                                                                         break;
1453                                                                                 }
1454                                                                                 cnf = cnf->next;
1455                                                                         }
1456                                                                         ast_mutex_unlock(&conflock);
1457                                                                         if (!found) {
1458                                                                                 /* At this point, we have a confno_tmp (static conference) that is empty */
1459                                                                                 if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) {
1460                                                                                 /* Case 1:  empty_no_pin and pin is nonexistant (NULL)
1461                                                                                  * Case 2:  empty_no_pin and pin is blank (but not NULL)
1462                                                                                  * Case 3:  not empty_no_pin
1463                                                                                  */
1464                                                                                         strncpy(confno, confno_tmp, sizeof(confno) - 1);
1465                                                                                         break;
1466                                                                                         /* XXX the map is not complete (but we do have a confno) */
1467                                                                                 }
1468                                                                         }
1469                                                                 }
1470                                                         } else {
1471                                                                 ast_log(LOG_ERROR, "Out of memory\n");
1472                                                         }
1473                                                 }
1474                                                 var = var->next;
1475                                         }
1476                                         ast_config_destroy(cfg);
1477                                 }
1478                         }
1479                         /* Select first conference number not in use */
1480                         if (ast_strlen_zero(confno) && dynamic) {
1481                                 for (i=0;i<1024;i++) {
1482                                         if (!map[i]) {
1483                                                 snprintf(confno, sizeof(confno), "%d", i);
1484                                                 break;
1485                                         }
1486                                 }
1487                         }
1488
1489                         /* Not found? */
1490                         if (ast_strlen_zero(confno)) {
1491                                 res = ast_streamfile(chan, "conf-noempty", chan->language);
1492                                 if (!res)
1493                                         ast_waitstream(chan, "");
1494                         } else {
1495                                 if (sscanf(confno, "%d", &confno_int) == 1) {
1496                                         res = ast_streamfile(chan, "conf-enteringno", chan->language);
1497                                         if (!res) {
1498                                                 ast_waitstream(chan, "");
1499                                                 res = ast_say_digits(chan, confno_int, "", chan->language);
1500                                         }
1501                                 } else {
1502                                         ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
1503                                 }
1504                         }
1505                 }
1506                 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
1507                         /* Prompt user for conference number */
1508                         res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
1509                         if (res < 0) {
1510                                 /* Don't try to validate when we catch an error */
1511                                 confno[0] = '\0';
1512                                 allowretry = 0;
1513                                 break;
1514                         }
1515                 }
1516                 if (!ast_strlen_zero(confno)) {
1517                         /* Check the validity of the conference */
1518                         cnf = find_conf(chan, confno, 1, dynamic, the_pin);
1519                         if (!cnf) {
1520                                 res = ast_streamfile(chan, "conf-invalid", chan->language);
1521                                 if (!res)
1522                                         ast_waitstream(chan, "");
1523                                 res = -1;
1524                                 if (allowretry)
1525                                         confno[0] = '\0';
1526                         } else {
1527                                 if (!ast_strlen_zero(cnf->pin)) {
1528                                         char pin[AST_MAX_EXTENSION]="";
1529                                         int j;
1530
1531                                         /* Allow the pin to be retried up to 3 times */
1532                                         for (j=0; j<3; j++) {
1533                                                 if (*the_pin) {
1534                                                         strncpy(pin, the_pin, sizeof(pin) - 1);
1535                                                         res = 0;
1536                                                 } else {
1537                                                         /* Prompt user for pin if pin is required */
1538                                                         res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
1539                                                 }
1540                                                 if (res >= 0) {
1541                                                         if (!strcasecmp(pin, cnf->pin)) {
1542                                                                 /* Pin correct */
1543                                                                 allowretry = 0;
1544                                                                 /* Run the conference */
1545                                                                 res = conf_run(chan, cnf, confflags);
1546                                                                 break;
1547                                                         } else {
1548                                                                 /* Pin invalid */
1549                                                                 res = ast_streamfile(chan, "conf-invalidpin", chan->language);
1550                                                                 if (!res)
1551                                                                         ast_waitstream(chan, AST_DIGIT_ANY);
1552                                                                 if (res < 0)
1553                                                                         break;
1554                                                                 pin[0] = res;
1555                                                                 pin[1] = '\0';
1556                                                                 res = -1;
1557                                                                 if (allowretry)
1558                                                                         confno[0] = '\0';
1559                                                         }
1560                                                 } else {
1561                                                         res = -1;
1562                                                         allowretry = 0;
1563                                                         break;
1564                                                 }
1565
1566                                                 /* Don't retry pin with a static pin */
1567                                                 if (*the_pin) {
1568                                                         break;
1569                                                 }
1570                                         }
1571                                 } else {
1572                                         /* No pin required */
1573                                         allowretry = 0;
1574
1575                                         /* Run the conference */
1576                                         res = conf_run(chan, cnf, confflags);
1577                                 }
1578                         }
1579                 }
1580         } while (allowretry);
1581         /* Do the conference */
1582         LOCAL_USER_REMOVE(u);
1583         return res;
1584 }
1585
1586 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
1587         struct ast_conf_user *user = NULL;
1588         char usrno[1024] = "";
1589         if (conf && callerident) {
1590                 user = conf->firstuser;
1591                 while(user) {
1592                         snprintf(usrno, sizeof(usrno), "%i", user->user_no);
1593                         if (strcmp(usrno, callerident) == 0)
1594                                 return user;
1595                         user = user->nextuser;
1596                 }
1597         }
1598         return NULL;
1599 }
1600
1601 /*--- admin_exec: The MeetMeadmin application */
1602 /* MeetMeAdmin(confno, command, caller) */
1603 static int admin_exec(struct ast_channel *chan, void *data) {
1604         char *params, *command = NULL, *caller = NULL, *conf = NULL;
1605         struct ast_conference *cnf;
1606         struct ast_conf_user *user = NULL;
1607
1608         ast_mutex_lock(&conflock);
1609         /* The param has the conference number the user and the command to execute */
1610         if (data && !ast_strlen_zero(data)) {           
1611                 params = ast_strdupa((char *) data);
1612                 conf = strsep(&params, "|");
1613                 command = strsep(&params, "|");
1614                 caller = strsep(&params, "|");
1615                 
1616                 if (!command) {
1617                         ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
1618                         ast_mutex_unlock(&conflock);
1619                         return -1;
1620                 }
1621                 cnf = confs;
1622                 while (cnf) {
1623                         if (strcmp(cnf->confno, conf) == 0) 
1624                                 break;
1625                         cnf = cnf->next;
1626                 }
1627                 
1628                 if (caller)
1629                         user = find_user(cnf, caller);
1630                 
1631                 if (cnf) {
1632                         switch((int) (*command)) {
1633                                 case 76: /* L: Lock */ 
1634                                         cnf->locked = 1;
1635                                         break;
1636                                 case 108: /* l: Unlock */ 
1637                                         cnf->locked = 0;
1638                                         break;
1639                                 case 75: /* K: kick all users*/
1640                                         user = cnf->firstuser;
1641                                         while(user) {
1642                                                 user->adminflags |= ADMINFLAG_KICKME;
1643                                                 if (user->nextuser) {
1644                                                         user = user->nextuser;
1645                                                 } else {
1646                                                         break;
1647                                                 }
1648                                         }
1649                                         break;
1650                                 case 101: /* e: Eject last user*/
1651                                         user = cnf->lastuser;
1652                                         if (!(user->userflags & CONFFLAG_ADMIN)) {
1653                                                 user->adminflags |= ADMINFLAG_KICKME;
1654                                                 break;
1655                                         } else
1656                                                 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
1657                                         break;
1658                                 case 77: /* M: Mute */ 
1659                                         if (user) {
1660                                                 user->adminflags |= ADMINFLAG_MUTED;
1661                                         } else {
1662                                                 ast_log(LOG_NOTICE, "Specified User not found!\n");
1663                                         }
1664                                         break;
1665                                 case 78: /* N: Mute all users */
1666                                         user = cnf->firstuser;
1667                                         while(user) {
1668                                                 if (user && !(user->userflags & CONFFLAG_ADMIN))
1669                                                         user->adminflags |= ADMINFLAG_MUTED;
1670                                                 if (user->nextuser) {
1671                                                         user = user->nextuser;
1672                                                 } else {
1673                                                         break;
1674                                                 }
1675                                         }
1676                                         break;                                  
1677                                 case 109: /* m: Unmute */ 
1678                                         if (user && (user->adminflags & ADMINFLAG_MUTED)) {
1679                                                 user->adminflags ^= ADMINFLAG_MUTED;
1680                                         } else {
1681                                                 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
1682                                         }
1683                                         break;
1684                                 case  110: /* n: Unmute all users */
1685                                         user = cnf->firstuser;
1686                                         while(user) {
1687                                                 if (user && (user-> adminflags & ADMINFLAG_MUTED)) {
1688                                                         user->adminflags ^= ADMINFLAG_MUTED;
1689                                                 }
1690                                                 if (user->nextuser) {
1691                                                         user = user->nextuser;
1692                                                 } else {
1693                                                         break;
1694                                                 }
1695                                         }
1696                                         break;
1697                                 case 107: /* k: Kick user */ 
1698                                         if (user) {
1699                                                 user->adminflags |= ADMINFLAG_KICKME;
1700                                         } else {
1701                                                 ast_log(LOG_NOTICE, "Specified User not found!");
1702                                         }
1703                                         break;
1704                         }
1705                 } else {
1706                         ast_log(LOG_NOTICE, "Conference Number not found\n");
1707                 }
1708         }
1709         ast_mutex_unlock(&conflock);
1710         return 0;
1711 }
1712
1713 static void *recordthread(void *args)
1714 {
1715         struct ast_conference *cnf;
1716         struct ast_frame *f=NULL;
1717         int flags;
1718         struct ast_filestream *s;
1719         int res=0;
1720
1721         cnf = (struct ast_conference *)args;
1722         if( !cnf || !cnf->chan ) {
1723                 pthread_exit(0);
1724         }
1725         ast_stopstream(cnf->chan);
1726         flags = O_CREAT|O_TRUNC|O_WRONLY;
1727         s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
1728
1729         if (s) {
1730                 cnf->recording = MEETME_RECORD_ACTIVE;
1731                 while (ast_waitfor(cnf->chan, -1) > -1) {
1732                         f = ast_read(cnf->chan);
1733                         if (!f) {
1734                                 res = -1;
1735                                 break;
1736                         }
1737                         if (f->frametype == AST_FRAME_VOICE) {
1738                                 res = ast_writestream(s, f);
1739                                 if (res) 
1740                                         break;
1741                         }
1742                         ast_frfree(f);
1743                         if (cnf->recording == MEETME_RECORD_TERMINATE) {
1744                                 ast_mutex_lock(&conflock);
1745                                 ast_mutex_unlock(&conflock);
1746                                 break;
1747                         }
1748                 }
1749                 cnf->recording = MEETME_RECORD_OFF;
1750                 ast_closestream(s);
1751         }
1752         pthread_exit(0);
1753 }
1754
1755 int unload_module(void)
1756 {
1757         STANDARD_HANGUP_LOCALUSERS;
1758         ast_cli_unregister(&cli_show_confs);
1759         ast_cli_unregister(&cli_conf);
1760         ast_unregister_application(app3);
1761         ast_unregister_application(app2);
1762         return ast_unregister_application(app);
1763 }
1764
1765 int load_module(void)
1766 {
1767         ast_cli_register(&cli_show_confs);
1768         ast_cli_register(&cli_conf);
1769         ast_register_application(app3, admin_exec, synopsis3, descrip3);
1770         ast_register_application(app2, count_exec, synopsis2, descrip2);
1771         return ast_register_application(app, conf_exec, synopsis, descrip);
1772 }
1773
1774
1775 char *description(void)
1776 {
1777         return tdesc;
1778 }
1779
1780 int usecount(void)
1781 {
1782         int res;
1783         STANDARD_USECOUNT(res);
1784         return res;
1785 }
1786
1787 char *key()
1788 {
1789         return ASTERISK_GPL_KEY;
1790 }
1791