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