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