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