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