2 * Asterisk -- A telephony toolkit for Linux.
4 * Meet me conference bridge
6 * Copyright (C) 1999 - 2005, Digium, Inc.
8 * Mark Spencer <markster@digium.com>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/pbx.h>
19 #include <asterisk/module.h>
20 #include <asterisk/config.h>
21 #include <asterisk/app.h>
22 #include <asterisk/dsp.h>
23 #include <asterisk/musiconhold.h>
24 #include <asterisk/manager.h>
25 #include <asterisk/options.h>
26 #include <asterisk/cli.h>
27 #include <asterisk/say.h>
28 #include <asterisk/utils.h>
34 #include <sys/ioctl.h>
35 #include "../asterisk.h"
36 #include "../astconf.h"
39 #include <linux/zaptel.h>
42 #endif /* __linux__ */
44 static char *tdesc = "MeetMe conference bridge";
46 static char *app = "MeetMe";
47 static char *app2 = "MeetMeCount";
48 static char *app3 = "MeetMeAdmin";
50 static char *synopsis = "MeetMe conference bridge";
51 static char *synopsis2 = "MeetMe participant count";
52 static char *synopsis3 = "MeetMe conference Administration";
54 static char *descrip =
55 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe conference.\n"
56 "If the conference number is omitted, the user will be prompted to enter\n"
58 "MeetMe returns 0 if user pressed # to exit (see option 'p'), otherwise -1.\n"
59 "Please note: A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING TO WORK!\n\n"
61 "The option string may contain zero or more of the following characters:\n"
62 " 'm' -- set monitor only mode (Listen only, no talking)\n"
63 " 't' -- set talk only mode. (Talk only, no listening)\n"
64 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
65 " 'i' -- announce user join/leave\n"
66 " 'p' -- allow user to exit the conference by pressing '#'\n"
67 " 'X' -- allow user to exit the conference by entering a valid single\n"
68 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
69 " if that variable is not defined.\n"
70 " 'd' -- dynamically add conference\n"
71 " 'D' -- dynamically add conference, prompting for a PIN\n"
72 " 'e' -- select an empty conference\n"
73 " 'E' -- select an empty pinless conference\n"
74 " 'v' -- video mode\n"
75 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
76 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
77 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is wav.\n"
78 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
79 " 'c' -- announce user(s) count on joining a conference\n"
80 " 'M' -- enable music on hold when the conference has a single caller\n"
81 " 'x' -- close the conference when last marked user exits\n"
82 " 'w' -- wait until the marked user enters the conference\n"
83 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
84 " Default: conf-background.agi\n"
85 " (Note: This does not work with non-Zap channels in the same conference)\n"
86 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
87 " 'a' -- set admin mode\n"
88 " 'A' -- set marked mode\n"
89 " 'P' -- always prompt for the pin even if it is specified\n";
91 static char *descrip2 =
92 " MeetMeCount(confno[|var]): Plays back the number of users in the specifiedi\n"
93 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
94 "will be returned in the variable. Returns 0 on success or -1 on a hangup.\n"
95 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
97 static char *descrip3 =
98 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
99 " 'K' -- Kick all users out of conference\n"
100 " 'k' -- Kick one user out of conference\n"
101 " 'e' -- Eject last user that joined\n"
102 " 'L' -- Lock conference\n"
103 " 'l' -- Unlock conference\n"
104 " 'M' -- Mute conference\n"
105 " 'm' -- Unmute conference\n"
106 " 'N' -- Mute entire conference (except admin)\n"
107 " 'n' -- Unmute entire conference (except admin)\n"
114 static struct ast_conference {
115 char confno[AST_MAX_EXTENSION]; /* Conference */
116 struct ast_channel *chan; /* Announcements channel */
117 int fd; /* Announcements fd */
118 int zapconf; /* Zaptel Conf # */
119 int users; /* Number of active users */
120 int markedusers; /* Number of marked users */
121 struct ast_conf_user *firstuser; /* Pointer to the first user struct */
122 struct ast_conf_user *lastuser; /* Pointer to the last user struct */
123 time_t start; /* Start time (s) */
124 int recording; /* recording status */
125 int isdynamic; /* Created on the fly? */
126 int locked; /* Is the conference locked? */
127 pthread_t recordthread; /* thread for recording */
128 pthread_attr_t attr; /* thread attribute */
129 char *recordingfilename; /* Filename to record the Conference into */
130 char *recordingformat; /* Format to record the Conference in */
131 char pin[AST_MAX_EXTENSION]; /* If protected by a PIN */
132 char pinadmin[AST_MAX_EXTENSION]; /* If protected by a admin PIN */
133 struct ast_conference *next;
136 struct ast_conf_user {
137 int user_no; /* User Number */
138 struct ast_conf_user *prevuser; /* Pointer to the previous user */
139 struct ast_conf_user *nextuser; /* Pointer to the next user */
140 int userflags; /* Flags as set in the conference */
141 int adminflags; /* Flags set by the Admin */
142 struct ast_channel *chan; /* Connected channel */
143 int talking; /* Is user talking */
144 char usrvalue[50]; /* Custom User Value */
145 char namerecloc[AST_MAX_EXTENSION]; /* Name Recorded file Location */
146 time_t jointime; /* Time the user joined the conference */
149 #define ADMINFLAG_MUTED (1 << 1) /* User is muted */
150 #define ADMINFLAG_KICKME (1 << 2) /* User is kicked */
151 #define MEETME_DELAYDETECTTALK 300
152 #define MEETME_DELAYDETECTENDTALK 1000
154 AST_MUTEX_DEFINE_STATIC(conflock);
156 static int admin_exec(struct ast_channel *chan, void *data);
158 static void *recordthread(void *args);
166 #define MEETME_RECORD_OFF 0
167 #define MEETME_RECORD_ACTIVE 1
168 #define MEETME_RECORD_TERMINATE 2
170 #define CONF_SIZE 320
172 #define CONFFLAG_ADMIN (1 << 1) /* If set the user has admin access on the conference */
173 #define CONFFLAG_MONITOR (1 << 2) /* If set the user can only receive audio from the conference */
174 #define CONFFLAG_POUNDEXIT (1 << 3) /* If set asterisk will exit conference when '#' is pressed */
175 #define CONFFLAG_STARMENU (1 << 4) /* If set asterisk will provide a menu to the user what '*' is pressed */
176 #define CONFFLAG_TALKER (1 << 5) /* If set the use can only send audio to the conference */
177 #define CONFFLAG_QUIET (1 << 6) /* If set there will be no enter or leave sounds */
178 #define CONFFLAG_VIDEO (1 << 7) /* Set to enable video mode */
179 #define CONFFLAG_AGI (1 << 8) /* Set to run AGI Script in Background */
180 #define CONFFLAG_MOH (1 << 9) /* Set to have music on hold when user is alone in conference */
181 #define CONFFLAG_MARKEDEXIT (1 << 10) /* If set the MeetMe will return if all marked with this flag left */
182 #define CONFFLAG_WAITMARKED (1 << 11) /* If set, the MeetMe will wait until a marked user enters */
183 #define CONFFLAG_EXIT_CONTEXT (1 << 12) /* If set, the MeetMe will exit to the specified context */
184 #define CONFFLAG_MARKEDUSER (1 << 13) /* If set, the user will be marked */
185 #define CONFFLAG_INTROUSER (1 << 14) /* If set, user will be ask record name on entry of conference */
186 #define CONFFLAG_RECORDCONF (1<< 15) /* If set, the MeetMe will be recorded */
187 #define CONFFLAG_MONITORTALKER (1 << 16) /* If set, the user will be monitored if the user is talking or not */
188 #define CONFFLAG_DYNAMIC (1 << 17)
189 #define CONFFLAG_DYNAMICPIN (1 << 18)
190 #define CONFFLAG_EMPTY (1 << 19)
191 #define CONFFLAG_EMPTYNOPIN (1 << 20)
192 #define CONFFLAG_ALWAYSPROMPT (1 << 21)
193 #define CONFFLAG_ANNOUNCEUSERCOUNT (1 << 22) /* If set, when user joins the conference, they will be told the number of users that are already in */
196 AST_DECLARE_OPTIONS(meetme_opts,{
197 ['a'] = { CONFFLAG_ADMIN },
198 ['c'] = { CONFFLAG_ANNOUNCEUSERCOUNT },
199 ['T'] = { CONFFLAG_MONITORTALKER },
200 ['i'] = { CONFFLAG_INTROUSER },
201 ['m'] = { CONFFLAG_MONITOR },
202 ['p'] = { CONFFLAG_POUNDEXIT },
203 ['s'] = { CONFFLAG_STARMENU },
204 ['t'] = { CONFFLAG_TALKER },
205 ['q'] = { CONFFLAG_QUIET },
206 ['M'] = { CONFFLAG_MOH },
207 ['x'] = { CONFFLAG_MARKEDEXIT },
208 ['X'] = { CONFFLAG_EXIT_CONTEXT },
209 ['A'] = { CONFFLAG_MARKEDUSER },
210 ['b'] = { CONFFLAG_AGI },
211 ['w'] = { CONFFLAG_WAITMARKED },
212 ['r'] = { CONFFLAG_RECORDCONF },
213 ['d'] = { CONFFLAG_DYNAMIC },
214 ['D'] = { CONFFLAG_DYNAMICPIN },
215 ['e'] = { CONFFLAG_EMPTY },
216 ['E'] = { CONFFLAG_EMPTYNOPIN },
217 ['P'] = { CONFFLAG_ALWAYSPROMPT },
220 static char *istalking(int x)
225 return "(unmonitored)";
227 return "(not talking)";
230 static int careful_write(int fd, unsigned char *data, int len)
235 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
236 res = ioctl(fd, ZT_IOMUX, &x);
238 res = write(fd, data, len);
240 if (errno != EAGAIN) {
241 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
252 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, int sound)
257 if (!chan->_softhangup)
258 res = ast_autoservice_start(chan);
259 ast_mutex_lock(&conflock);
274 careful_write(conf->fd, data, len);
275 ast_mutex_unlock(&conflock);
277 ast_autoservice_stop(chan);
280 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic)
282 struct ast_conference *cnf;
283 struct zt_confinfo ztc;
284 ast_mutex_lock(&conflock);
287 if (!strcmp(confno, cnf->confno))
291 if (!cnf && (make || dynamic)) {
292 cnf = malloc(sizeof(struct ast_conference));
295 memset(cnf, 0, sizeof(struct ast_conference));
296 strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1);
297 strncpy(cnf->pin, pin, sizeof(cnf->pin) - 1);
298 strncpy(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin) - 1);
299 cnf->markedusers = 0;
300 cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo", NULL);
302 cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */
304 ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
305 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
307 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
313 memset(&ztc, 0, sizeof(ztc));
314 /* Setup a new zap conference */
317 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
318 if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
319 ast_log(LOG_WARNING, "Error setting conference\n");
321 ast_hangup(cnf->chan);
328 /* Fill the conference struct */
329 cnf->start = time(NULL);
330 cnf->zapconf = ztc.confno;
331 cnf->isdynamic = dynamic;
332 cnf->firstuser = NULL;
333 cnf->lastuser = NULL;
335 if (option_verbose > 2)
336 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
340 ast_log(LOG_WARNING, "Out of memory\n");
343 ast_mutex_unlock(&conflock);
347 static int confs_show(int fd, int argc, char **argv)
349 ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
350 return RESULT_SUCCESS;
353 static char show_confs_usage[] =
354 "Deprecated! Please use 'meetme' instead.\n";
356 static struct ast_cli_entry cli_show_confs = {
357 { "show", "conferences", NULL }, confs_show,
358 "Show status of conferences", show_confs_usage, NULL };
360 static int conf_cmd(int fd, int argc, char **argv) {
361 /* Process the command */
362 struct ast_conference *cnf;
363 struct ast_conf_user *user;
365 int i = 0, total = 0;
367 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
368 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
369 char cmdline[1024] = "";
372 ast_cli(fd, "Invalid Arguments.\n");
373 /* Check for length so no buffer will overflow... */
374 for (i = 0; i < argc; i++) {
375 if (strlen(argv[i]) > 100)
376 ast_cli(fd, "Invalid Arguments.\n");
379 /* 'MeetMe': List all the conferences */
383 ast_cli(fd, "No active MeetMe conferences.\n");
384 return RESULT_SUCCESS;
386 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
388 if (cnf->markedusers == 0)
389 strncpy(cmdline, "N/A ", sizeof(cmdline) - 1);
391 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
392 hr = (now - cnf->start) / 3600;
393 min = ((now - cnf->start) % 3600) / 60;
394 sec = (now - cnf->start) % 60;
396 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
401 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
402 return RESULT_SUCCESS;
405 return RESULT_SHOWUSAGE;
406 strncpy(cmdline, argv[2], sizeof(cmdline) - 1); /* Argv 2: conference number */
407 if (strstr(argv[1], "lock")) {
408 if (strcmp(argv[1], "lock") == 0) {
410 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
413 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
415 } else if (strstr(argv[1], "mute")) {
417 return RESULT_SHOWUSAGE;
418 if (strcmp(argv[1], "mute") == 0) {
420 if (strcmp(argv[3], "all") == 0) {
421 strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
423 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
424 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
428 if (strcmp(argv[3], "all") == 0) {
429 strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
431 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
432 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
435 } else if (strcmp(argv[1], "kick") == 0) {
437 return RESULT_SHOWUSAGE;
438 if (strcmp(argv[3], "all") == 0) {
440 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
442 /* Kick a single user */
443 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
444 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
446 } else if(strcmp(argv[1], "list") == 0) {
447 /* List all the users in a conference */
449 ast_cli(fd, "No active conferences.\n");
450 return RESULT_SUCCESS;
453 /* Find the right conference */
455 if (strcmp(cnf->confno, argv[2]) == 0)
460 ast_cli(fd, "No such conference: %s.\n",argv[2]);
461 return RESULT_SUCCESS;
464 /* Show all the users */
465 user = cnf->firstuser;
467 ast_cli(fd, "User #: %i Channel: %s %s %s %s %s\n", user->user_no, user->chan->name, (user->userflags & CONFFLAG_ADMIN) ? "(Admin)" : "", (user->userflags & CONFFLAG_MONITOR) ? "(Listen only)" : "", (user->adminflags & ADMINFLAG_MUTED) ? "(Admn Muted)" : "", istalking(user->talking));
468 user = user->nextuser;
470 ast_cli(fd,"%d users in that conference.\n",cnf->users);
471 return RESULT_SUCCESS;
473 return RESULT_SHOWUSAGE;
474 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
475 admin_exec(NULL, cmdline);
479 static char *complete_confcmd(char *line, char *word, int pos, int state) {
480 #define CONF_COMMANDS 6
481 int which = 0, x = 0;
482 struct ast_conference *cnf = NULL;
483 struct ast_conf_user *usr = NULL;
486 char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"};
491 for (x = 0;x < CONF_COMMANDS; x++) {
492 if (!strncasecmp(cmds[x], word, strlen(word))) {
493 if (++which > state) {
494 return strdup(cmds[x]);
498 } else if (pos == 2) {
499 /* Conference Number */
500 ast_mutex_lock(&conflock);
503 if (!strncasecmp(word, cnf->confno, strlen(word))) {
509 ast_mutex_unlock(&conflock);
510 return cnf ? strdup(cnf->confno) : NULL;
511 } else if (pos == 3) {
512 /* User Number || Conf Command option*/
513 if (strstr(line, "mute") || strstr(line, "kick")) {
514 if ((state == 0) && (strstr(line, "kick") || strstr(line,"mute")) && !(strncasecmp(word, "all", strlen(word)))) {
515 return strdup("all");
518 ast_mutex_lock(&conflock);
521 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
522 myline = ast_strdupa(line);
523 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
524 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
529 if (strcmp(confno, cnf->confno) == 0) {
535 /* Search for the user */
536 usr = cnf->firstuser;
538 snprintf(usrno, sizeof(usrno), "%i", usr->user_no);
539 if (!strncasecmp(word, usrno, strlen(word))) {
546 ast_mutex_unlock(&conflock);
547 return usr ? strdup(usrno) : NULL;
553 static char conf_usage[] =
554 "Usage: meetme (un)lock|(un)mute|kick|list <confno> <usernumber>\n"
555 " Executes a command for the conference or on a conferee\n";
557 static struct ast_cli_entry cli_conf = {
558 { "meetme", NULL, NULL }, conf_cmd,
559 "Execute a command on a conference or conferee", conf_usage, complete_confcmd };
561 static void conf_flush(int fd)
565 if (ioctl(fd, ZT_FLUSH, &x))
566 ast_log(LOG_WARNING, "Error flushing channel\n");
569 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
571 struct ast_conference *prev=NULL, *cur;
572 struct ast_conf_user *user = malloc(sizeof(struct ast_conf_user));
573 struct ast_conf_user *usr = NULL;
575 struct zt_confinfo ztc, ztc_empty;
577 struct ast_channel *c;
590 int currentmarked = 0;
594 int using_pseudo = 0;
596 struct ast_dsp *dsp=NULL;
600 char *agifiledefault = "conf-background.agi";
601 char meetmesecs[30] = "";
602 char exitcontext[AST_MAX_EXTENSION] = "";
603 char recordingtmp[AST_MAX_EXTENSION] = "";
607 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
608 char *buf = __buf + AST_FRIENDLY_OFFSET;
611 ast_log(LOG_ERROR, "Out of memory\n");
614 memset(user, 0, sizeof(struct ast_conf_user));
616 if (confflags & CONFFLAG_RECORDCONF && conf->recording !=MEETME_RECORD_ACTIVE) {
617 conf->recordingfilename = pbx_builtin_getvar_helper(chan,"MEETME_RECORDINGFILE");
618 if (!conf->recordingfilename) {
619 snprintf(recordingtmp,sizeof(recordingtmp),"meetme-conf-rec-%s-%s",conf->confno,chan->uniqueid);
620 conf->recordingfilename = ast_strdupa(recordingtmp);
622 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
623 if (!conf->recordingformat) {
624 snprintf(recordingtmp,sizeof(recordingtmp), "wav");
625 conf->recordingformat = ast_strdupa(recordingtmp);
627 pthread_attr_init(&conf->attr);
628 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
629 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n", conf->confno, conf->recordingfilename, conf->recordingformat);
630 ast_pthread_create(&conf->recordthread, &conf->attr, recordthread, conf);
633 user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */
635 time(&user->jointime);
638 /* Sorry, but this confernce is locked! */
639 if (!ast_streamfile(chan, "conf-locked", chan->language))
640 ast_waitstream(chan, "");
645 if (confflags & CONFFLAG_MARKEDUSER)
648 ast_mutex_lock(&conflock);
649 if (conf->firstuser == NULL) {
650 /* Fill the first new User struct */
652 user->nextuser = NULL;
653 user->prevuser = NULL;
654 conf->firstuser = user;
655 conf->lastuser = user;
657 /* Fill the new user struct */
658 user->user_no = conf->lastuser->user_no + 1;
659 user->prevuser = conf->lastuser;
660 user->nextuser = NULL;
661 if (conf->lastuser->nextuser != NULL) {
662 ast_log(LOG_WARNING, "Error in User Management!\n");
663 ast_mutex_unlock(&conflock);
666 conf->lastuser->nextuser = user;
667 conf->lastuser = user;
671 user->userflags = confflags;
672 user->adminflags = 0;
674 ast_mutex_unlock(&conflock);
675 origquiet = confflags & CONFFLAG_QUIET;
676 if (confflags & CONFFLAG_EXIT_CONTEXT) {
677 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
678 strncpy(exitcontext, agifile, sizeof(exitcontext) - 1);
679 else if (!ast_strlen_zero(chan->macrocontext))
680 strncpy(exitcontext, chan->macrocontext, sizeof(exitcontext) - 1);
682 strncpy(exitcontext, chan->context, sizeof(exitcontext) - 1);
685 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
686 snprintf(user->namerecloc,sizeof(user->namerecloc),"%s/meetme/meetme-username-%s-%d",ast_config_AST_SPOOL_DIR,conf->confno,user->user_no);
687 ast_record_review(chan,"vm-rec-name",user->namerecloc, 10,"sln", &duration, NULL);
690 if (!(confflags & CONFFLAG_QUIET)) {
691 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
692 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
693 ast_waitstream(chan, "");
694 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
695 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
696 ast_waitstream(chan, "");
699 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
702 if (conf->users == 2) {
703 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
704 res = ast_waitstream(chan, AST_DIGIT_ANY);
711 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
712 res = ast_waitstream(chan, AST_DIGIT_ANY);
719 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
725 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
726 res = ast_waitstream(chan, AST_DIGIT_ANY);
735 /* Set it into linear mode (write) */
736 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
737 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
741 /* Set it into linear mode (read) */
742 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
743 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
746 ast_indicate(chan, -1);
747 retryzap = strcasecmp(chan->type, "Zap");
749 origfd = chan->fds[0];
751 fd = open("/dev/zap/pseudo", O_RDWR);
753 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
757 /* Make non-blocking */
758 flags = fcntl(fd, F_GETFL);
760 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
764 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
765 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
769 /* Setup buffering information */
770 memset(&bi, 0, sizeof(bi));
771 bi.bufsize = CONF_SIZE/2;
772 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
773 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
775 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
776 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
781 if (ioctl(fd, ZT_SETLINEAR, &x)) {
782 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
788 /* XXX Make sure we're not running on a pseudo channel XXX */
792 memset(&ztc, 0, sizeof(ztc));
793 memset(&ztc_empty, 0, sizeof(ztc_empty));
794 /* Check to see if we're in a conference... */
796 if (ioctl(fd, ZT_GETCONF, &ztc)) {
797 ast_log(LOG_WARNING, "Error getting conference\n");
802 /* Whoa, already in a conference... Retry... */
804 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
809 memset(&ztc, 0, sizeof(ztc));
810 /* Add us to the conference */
812 ztc.confno = conf->zapconf;
813 ast_mutex_lock(&conflock);
814 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER) && conf->users > 1) {
815 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
816 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
817 ast_waitstream(conf->chan, "");
818 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
819 ast_waitstream(conf->chan, "");
823 if (confflags & CONFFLAG_MONITOR)
824 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
825 else if (confflags & CONFFLAG_TALKER)
826 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
828 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
830 if (ioctl(fd, ZT_SETCONF, &ztc)) {
831 ast_log(LOG_WARNING, "Error setting conference\n");
833 ast_mutex_unlock(&conflock);
836 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
838 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
843 chan->name, chan->uniqueid, conf->confno, user->user_no);
845 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
847 if (!(confflags & CONFFLAG_QUIET))
848 if (!(confflags & CONFFLAG_WAITMARKED) || (conf->markedusers >= 1))
849 conf_play(chan, conf, ENTER);
852 ast_mutex_unlock(&conflock);
853 if (confflags & CONFFLAG_AGI) {
855 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
856 or use default filename of conf-background.agi */
858 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
860 agifile = agifiledefault;
862 if (!strcasecmp(chan->type,"Zap")) {
863 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
865 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
867 /* Find a pointer to the agi app and execute the script */
868 app = pbx_findapp("agi");
870 ret = pbx_exec(chan, app, agifile, 1);
872 ast_log(LOG_WARNING, "Could not find application (agi)\n");
875 if (!strcasecmp(chan->type,"Zap")) {
876 /* Remove CONFMUTE mode on Zap channel */
878 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
881 if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
882 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
884 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
886 if (confflags & CONFFLAG_MONITORTALKER && !(dsp = ast_dsp_new())) {
887 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
893 currentmarked = conf->markedusers;
894 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_MARKEDUSER) && (confflags & CONFFLAG_WAITMARKED) && lastmarked == 0) {
895 if (currentmarked == 1 && conf->users > 1) {
896 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
897 if (conf->users - 1 == 1) {
898 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
899 ast_waitstream(chan, "");
901 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
902 ast_waitstream(chan, "");
905 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
906 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
907 ast_waitstream(chan, "");
910 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
912 /* Update the struct with the actual confflags */
913 user->userflags = confflags;
915 if (confflags & CONFFLAG_WAITMARKED) {
916 if(currentmarked == 0) {
917 if (lastmarked != 0) {
918 if (!(confflags & CONFFLAG_QUIET))
919 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
920 ast_waitstream(chan, "");
921 if(confflags & CONFFLAG_MARKEDEXIT)
924 ztc.confmode = ZT_CONF_CONF;
925 if (ioctl(fd, ZT_SETCONF, &ztc)) {
926 ast_log(LOG_WARNING, "Error setting conference\n");
932 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
933 ast_moh_start(chan, NULL);
936 ztc.confmode = ZT_CONF_CONF;
937 if (ioctl(fd, ZT_SETCONF, &ztc)) {
938 ast_log(LOG_WARNING, "Error setting conference\n");
943 } else if(currentmarked >= 1 && lastmarked == 0) {
944 if (confflags & CONFFLAG_MONITOR)
945 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
946 else if (confflags & CONFFLAG_TALKER)
947 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
949 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
950 if (ioctl(fd, ZT_SETCONF, &ztc)) {
951 ast_log(LOG_WARNING, "Error setting conference\n");
955 if (musiconhold && (confflags & CONFFLAG_MOH)) {
959 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
960 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
961 ast_waitstream(chan, "");
962 conf_play(chan, conf, ENTER);
967 /* trying to add moh for single person conf */
968 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
969 if (conf->users == 1) {
970 if (musiconhold == 0) {
971 ast_moh_start(chan, NULL);
982 /* Leave if the last marked user left */
983 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
988 /* Check if the admin changed my modes */
989 if (user->adminflags) {
990 /* Set the new modes */
991 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
992 ztc.confmode ^= ZT_CONF_TALKER;
993 if (ioctl(fd, ZT_SETCONF, &ztc)) {
994 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
999 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1000 ztc.confmode |= ZT_CONF_TALKER;
1001 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1002 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1007 if (user->adminflags & ADMINFLAG_KICKME) {
1008 /* You have been kicked. */
1009 if (!ast_streamfile(chan, "conf-kicked", chan->language))
1010 ast_waitstream(chan, "");
1014 } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1015 ztc.confmode |= ZT_CONF_TALKER;
1016 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1017 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1024 if (c->fds[0] != origfd) {
1026 /* Kill old pseudo */
1029 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
1037 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1038 if (confflags & CONFFLAG_MONITORTALKER) {
1040 if (user->talking == -1)
1043 res = ast_dsp_silence(dsp, f, &totalsilence);
1044 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1046 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1051 chan->name, chan->uniqueid, conf->confno, user->user_no);
1053 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1055 manager_event(EVENT_FLAG_CALL, "MeetmeStopTalking",
1060 chan->name, chan->uniqueid, conf->confno, user->user_no);
1064 /* Carefully write */
1065 careful_write(fd, f->data, f->datalen);
1067 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
1069 tmp[0] = f->subclass;
1071 if (ast_exists_extension(chan, exitcontext, tmp, 1, chan->cid.cid_num)) {
1072 strncpy(chan->context, exitcontext, sizeof(chan->context) - 1);
1073 strncpy(chan->exten, tmp, sizeof(chan->exten) - 1);
1078 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
1081 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
1082 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
1083 ast_log(LOG_WARNING, "Error setting conference\n");
1085 ast_mutex_unlock(&conflock);
1091 if ((confflags & CONFFLAG_ADMIN)) {
1095 /* Record this sound! */
1096 if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
1097 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1104 case '1': /* Un/Mute */
1106 if (ztc.confmode & ZT_CONF_TALKER) {
1107 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1108 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1110 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1111 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1113 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1114 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1118 if (ztc.confmode & ZT_CONF_TALKER) {
1119 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1120 ast_waitstream(chan, "");
1122 if (!ast_streamfile(chan, "conf-muted", chan->language))
1123 ast_waitstream(chan, "");
1126 case '2': /* Un/Lock the Conference */
1130 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1131 ast_waitstream(chan, "");
1134 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1135 ast_waitstream(chan, "");
1138 case '3': /* Eject last user */
1140 usr = conf->lastuser;
1141 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1142 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1143 ast_waitstream(chan, "");
1145 usr->adminflags |= ADMINFLAG_KICKME;
1146 ast_stopstream(chan);
1150 /* Play an error message! */
1151 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1152 ast_waitstream(chan, "");
1160 /* Record this sound! */
1161 if (!ast_streamfile(chan, "conf-usermenu", chan->language))
1162 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1169 case '1': /* Un/Mute */
1171 if (ztc.confmode & ZT_CONF_TALKER) {
1172 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1173 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1174 } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
1175 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1176 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1178 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1179 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1183 if (ztc.confmode & ZT_CONF_TALKER) {
1184 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1185 ast_waitstream(chan, "");
1187 if (!ast_streamfile(chan, "conf-muted", chan->language))
1188 ast_waitstream(chan, "");
1193 /* Play an error message! */
1194 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1195 ast_waitstream(chan, "");
1201 ast_moh_start(chan, NULL);
1204 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1205 ast_log(LOG_WARNING, "Error setting conference\n");
1207 ast_mutex_unlock(&conflock);
1211 } else if (option_debug) {
1212 ast_log(LOG_DEBUG, "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",chan->name,f->frametype,f->subclass);
1215 } else if (outfd > -1) {
1216 res = read(outfd, buf, CONF_SIZE);
1218 memset(&fr, 0, sizeof(fr));
1219 fr.frametype = AST_FRAME_VOICE;
1220 fr.subclass = AST_FORMAT_SLINEAR;
1224 fr.offset = AST_FRIENDLY_OFFSET;
1225 if (ast_write(chan, &fr) < 0) {
1226 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1230 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1232 lastmarked = currentmarked;
1238 /* Take out of conference */
1239 /* Add us to the conference */
1243 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1244 ast_log(LOG_WARNING, "Error setting conference\n");
1248 ast_mutex_lock(&conflock);
1249 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1250 conf_play(chan, conf, LEAVE);
1252 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
1253 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
1254 if ((conf->chan) && (conf->users > 1)) {
1255 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1256 ast_waitstream(conf->chan, "");
1257 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1258 ast_waitstream(conf->chan, "");
1260 ast_filedelete(user->namerecloc, NULL);
1263 ast_mutex_unlock(&conflock);
1267 ast_mutex_lock(&conflock);
1268 if (confflags & CONFFLAG_MONITORTALKER && dsp)
1271 if (user->user_no) { /* Only cleanup users who really joined! */
1272 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
1277 chan->name, chan->uniqueid, conf->confno, user->user_no);
1280 if (confflags & CONFFLAG_MARKEDUSER)
1281 conf->markedusers--;
1284 /* No more users -- close this one out */
1288 prev->next = conf->next;
1297 ast_log(LOG_WARNING, "Conference not found\n");
1298 if (conf->recording == MEETME_RECORD_ACTIVE) {
1299 conf->recording = MEETME_RECORD_TERMINATE;
1300 ast_mutex_unlock(&conflock);
1302 ast_mutex_lock(&conflock);
1303 if (conf->recording == MEETME_RECORD_OFF)
1305 ast_mutex_unlock(&conflock);
1309 ast_hangup(conf->chan);
1314 /* Remove the user struct */
1315 if (user == conf->firstuser) {
1316 if (user->nextuser) {
1317 /* There is another entry */
1318 user->nextuser->prevuser = NULL;
1320 /* We are the only entry */
1321 conf->lastuser = NULL;
1323 /* In either case */
1324 conf->firstuser = user->nextuser;
1325 } else if (user == conf->lastuser){
1327 user->prevuser->nextuser = NULL;
1329 ast_log(LOG_ERROR, "Bad bad bad! We're the last, not the first, but nobody before us??\n");
1330 conf->lastuser = user->prevuser;
1333 user->nextuser->prevuser = user->prevuser;
1335 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
1337 user->prevuser->nextuser = user->nextuser;
1339 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
1342 /* Return the number of seconds the user was in the conf */
1343 snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (time(NULL) - user->jointime));
1344 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1347 ast_mutex_unlock(&conflock);
1351 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
1353 struct ast_config *cfg;
1354 struct ast_variable *var;
1355 struct ast_conference *cnf;
1357 /* Check first in the conference list */
1358 ast_mutex_lock(&conflock);
1361 if (!strcmp(confno, cnf->confno))
1365 ast_mutex_unlock(&conflock);
1369 /* No need to parse meetme.conf */
1370 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1372 if (dynamic_pin[0] == 'q') {
1373 /* Query the user to enter a PIN */
1374 ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
1376 cnf = build_conf(confno, dynamic_pin, "", make, dynamic);
1378 cnf = build_conf(confno, "", "", make, dynamic);
1381 /* Check the config */
1382 cfg = ast_config_load("meetme.conf");
1384 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
1387 var = ast_variable_browse(cfg, "rooms");
1389 if (!strcasecmp(var->name, "conf")) {
1390 /* Separate the PIN */
1391 char *pin, *pinadmin, *conf;
1393 if ((pinadmin = ast_strdupa(var->value))) {
1394 conf = strsep(&pinadmin, "|,");
1395 pin = strsep(&pinadmin, "|,");
1396 if (!strcasecmp(conf, confno)) {
1397 /* Bingo it's a valid conference */
1400 cnf = build_conf(confno, pin, pinadmin, make, dynamic);
1402 cnf = build_conf(confno, pin, "", make, dynamic);
1405 cnf = build_conf(confno, "", pinadmin, make, dynamic);
1407 cnf = build_conf(confno, "", "", make, dynamic);
1415 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1417 ast_config_destroy(cfg);
1419 } else if (dynamic_pin) {
1420 /* Correct for the user selecting 'D' instead of 'd' to have
1421 someone join into a conference that has already been created
1423 if (dynamic_pin[0] == 'q')
1424 dynamic_pin[0] = '\0';
1429 /*--- count_exec: The MeetmeCount application */
1430 static int count_exec(struct ast_channel *chan, void *data)
1432 struct localuser *u;
1434 struct ast_conference *conf;
1436 char *confnum, *localdata;
1439 if (!data || ast_strlen_zero(data)) {
1440 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1443 localdata = ast_strdupa(data);
1445 confnum = strsep(&localdata,"|");
1446 conf = find_conf(chan, confnum, 0, 0, NULL);
1448 count = conf->users;
1452 if (localdata && !ast_strlen_zero(localdata)){
1453 /* have var so load it and exit */
1454 snprintf(val,sizeof(val), "%i",count);
1455 pbx_builtin_setvar_helper(chan, localdata,val);
1457 if (chan->_state != AST_STATE_UP)
1459 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1461 LOCAL_USER_REMOVE(u);
1465 /*--- conf_exec: The meetme() application */
1466 static int conf_exec(struct ast_channel *chan, void *data)
1469 struct localuser *u;
1470 char confno[AST_MAX_EXTENSION] = "";
1473 struct ast_conference *cnf;
1474 struct ast_flags confflags = {0};
1476 int empty = 0, empty_no_pin = 0;
1477 int always_prompt = 0;
1478 char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
1480 if (!data || ast_strlen_zero(data)) {
1487 if (chan->_state != AST_STATE_UP)
1490 info = ast_strdupa((char *)notdata);
1493 char *tmp = strsep(&info, "|");
1494 strncpy(confno, tmp, sizeof(confno) - 1);
1495 if (ast_strlen_zero(confno)) {
1500 inflags = strsep(&info, "|");
1502 inpin = strsep(&info, "|");
1504 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
1507 ast_parseoptions(meetme_opts, &confflags, NULL, inflags);
1508 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
1509 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !inpin)
1510 strncpy(the_pin, "q", sizeof(the_pin) - 1);
1512 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
1513 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
1514 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
1522 struct ast_config *cfg;
1523 struct ast_variable *var;
1526 memset(map, 0, sizeof(map));
1528 ast_mutex_lock(&conflock);
1531 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
1532 /* Disqualify in use conference */
1533 if (confno_int >= 0 && confno_int < 1024)
1538 ast_mutex_unlock(&conflock);
1540 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
1541 if ((empty_no_pin) || (!dynamic)) {
1542 cfg = ast_config_load("meetme.conf");
1544 var = ast_variable_browse(cfg, "rooms");
1546 if (!strcasecmp(var->name, "conf")) {
1547 char *stringp = ast_strdupa(var->value);
1549 char *confno_tmp = strsep(&stringp, "|,");
1551 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
1552 if ((confno_int >= 0) && (confno_int < 1024)) {
1553 if (stringp && empty_no_pin) {
1559 /* For static: run through the list and see if this conference is empty */
1560 ast_mutex_lock(&conflock);
1563 if (!strcmp(confno_tmp, cnf->confno)) {
1564 /* The conference exists, therefore it's not empty */
1570 ast_mutex_unlock(&conflock);
1572 /* At this point, we have a confno_tmp (static conference) that is empty */
1573 if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) {
1574 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
1575 * Case 2: empty_no_pin and pin is blank (but not NULL)
1576 * Case 3: not empty_no_pin
1578 strncpy(confno, confno_tmp, sizeof(confno) - 1);
1580 /* XXX the map is not complete (but we do have a confno) */
1585 ast_log(LOG_ERROR, "Out of memory\n");
1590 ast_config_destroy(cfg);
1593 /* Select first conference number not in use */
1594 if (ast_strlen_zero(confno) && dynamic) {
1595 for (i=0;i<1024;i++) {
1597 snprintf(confno, sizeof(confno), "%d", i);
1604 if (ast_strlen_zero(confno)) {
1605 res = ast_streamfile(chan, "conf-noempty", chan->language);
1607 ast_waitstream(chan, "");
1609 if (sscanf(confno, "%d", &confno_int) == 1) {
1610 res = ast_streamfile(chan, "conf-enteringno", chan->language);
1612 ast_waitstream(chan, "");
1613 res = ast_say_digits(chan, confno_int, "", chan->language);
1616 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
1620 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
1621 /* Prompt user for conference number */
1622 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
1624 /* Don't try to validate when we catch an error */
1630 if (!ast_strlen_zero(confno)) {
1631 /* Check the validity of the conference */
1632 cnf = find_conf(chan, confno, 1, dynamic, the_pin);
1634 res = ast_streamfile(chan, "conf-invalid", chan->language);
1636 ast_waitstream(chan, "");
1641 if ((!ast_strlen_zero(cnf->pin) && !ast_test_flag(&confflags, CONFFLAG_ADMIN)) || (!ast_strlen_zero(cnf->pinadmin) && ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
1642 char pin[AST_MAX_EXTENSION]="";
1645 /* Allow the pin to be retried up to 3 times */
1646 for (j=0; j<3; j++) {
1647 if (*the_pin && (always_prompt==0)) {
1648 strncpy(pin, the_pin, sizeof(pin) - 1);
1651 /* Prompt user for pin if pin is required */
1652 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
1655 if (!strcasecmp(pin, cnf->pin) || (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))) {
1659 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
1660 ast_set_flag(&confflags, CONFFLAG_ADMIN);
1661 /* Run the conference */
1662 res = conf_run(chan, cnf, confflags.flags);
1666 res = ast_streamfile(chan, "conf-invalidpin", chan->language);
1668 ast_waitstream(chan, AST_DIGIT_ANY);
1683 /* Don't retry pin with a static pin */
1684 if (*the_pin && (always_prompt==0)) {
1689 /* No pin required */
1692 /* Run the conference */
1693 res = conf_run(chan, cnf, confflags.flags);
1697 } while (allowretry);
1698 /* Do the conference */
1699 LOCAL_USER_REMOVE(u);
1703 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
1704 struct ast_conf_user *user = NULL;
1705 char usrno[1024] = "";
1706 if (conf && callerident) {
1707 user = conf->firstuser;
1709 snprintf(usrno, sizeof(usrno), "%i", user->user_no);
1710 if (strcmp(usrno, callerident) == 0)
1712 user = user->nextuser;
1718 /*--- admin_exec: The MeetMeadmin application */
1719 /* MeetMeAdmin(confno, command, caller) */
1720 static int admin_exec(struct ast_channel *chan, void *data) {
1721 char *params, *command = NULL, *caller = NULL, *conf = NULL;
1722 struct ast_conference *cnf;
1723 struct ast_conf_user *user = NULL;
1725 ast_mutex_lock(&conflock);
1726 /* The param has the conference number the user and the command to execute */
1727 if (data && !ast_strlen_zero(data)) {
1728 params = ast_strdupa((char *) data);
1729 conf = strsep(¶ms, "|");
1730 command = strsep(¶ms, "|");
1731 caller = strsep(¶ms, "|");
1734 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
1735 ast_mutex_unlock(&conflock);
1740 if (strcmp(cnf->confno, conf) == 0)
1746 user = find_user(cnf, caller);
1749 switch((int) (*command)) {
1750 case 76: /* L: Lock */
1753 case 108: /* l: Unlock */
1756 case 75: /* K: kick all users*/
1757 user = cnf->firstuser;
1759 user->adminflags |= ADMINFLAG_KICKME;
1760 if (user->nextuser) {
1761 user = user->nextuser;
1767 case 101: /* e: Eject last user*/
1768 user = cnf->lastuser;
1769 if (!(user->userflags & CONFFLAG_ADMIN)) {
1770 user->adminflags |= ADMINFLAG_KICKME;
1773 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
1775 case 77: /* M: Mute */
1777 user->adminflags |= ADMINFLAG_MUTED;
1779 ast_log(LOG_NOTICE, "Specified User not found!\n");
1782 case 78: /* N: Mute all users */
1783 user = cnf->firstuser;
1785 if (user && !(user->userflags & CONFFLAG_ADMIN))
1786 user->adminflags |= ADMINFLAG_MUTED;
1787 if (user->nextuser) {
1788 user = user->nextuser;
1794 case 109: /* m: Unmute */
1795 if (user && (user->adminflags & ADMINFLAG_MUTED)) {
1796 user->adminflags ^= ADMINFLAG_MUTED;
1798 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
1801 case 110: /* n: Unmute all users */
1802 user = cnf->firstuser;
1804 if (user && (user-> adminflags & ADMINFLAG_MUTED)) {
1805 user->adminflags ^= ADMINFLAG_MUTED;
1807 if (user->nextuser) {
1808 user = user->nextuser;
1814 case 107: /* k: Kick user */
1816 user->adminflags |= ADMINFLAG_KICKME;
1818 ast_log(LOG_NOTICE, "Specified User not found!");
1823 ast_log(LOG_NOTICE, "Conference Number not found\n");
1826 ast_mutex_unlock(&conflock);
1830 static void *recordthread(void *args)
1832 struct ast_conference *cnf;
1833 struct ast_frame *f=NULL;
1835 struct ast_filestream *s;
1838 cnf = (struct ast_conference *)args;
1839 if( !cnf || !cnf->chan ) {
1842 ast_stopstream(cnf->chan);
1843 flags = O_CREAT|O_TRUNC|O_WRONLY;
1844 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
1847 cnf->recording = MEETME_RECORD_ACTIVE;
1848 while (ast_waitfor(cnf->chan, -1) > -1) {
1849 f = ast_read(cnf->chan);
1854 if (f->frametype == AST_FRAME_VOICE) {
1855 res = ast_writestream(s, f);
1860 if (cnf->recording == MEETME_RECORD_TERMINATE) {
1861 ast_mutex_lock(&conflock);
1862 ast_mutex_unlock(&conflock);
1866 cnf->recording = MEETME_RECORD_OFF;
1872 int unload_module(void)
1874 STANDARD_HANGUP_LOCALUSERS;
1875 ast_cli_unregister(&cli_show_confs);
1876 ast_cli_unregister(&cli_conf);
1877 ast_unregister_application(app3);
1878 ast_unregister_application(app2);
1879 return ast_unregister_application(app);
1882 int load_module(void)
1884 ast_cli_register(&cli_show_confs);
1885 ast_cli_register(&cli_conf);
1886 ast_register_application(app3, admin_exec, synopsis3, descrip3);
1887 ast_register_application(app2, count_exec, synopsis2, descrip2);
1888 return ast_register_application(app, conf_exec, synopsis, descrip);
1892 char *description(void)
1900 STANDARD_USECOUNT(res);
1906 return ASTERISK_GPL_KEY;