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