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