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