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