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