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