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;
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 /* Check to see if we're in a conference... */
795 if (ioctl(fd, ZT_GETCONF, &ztc)) {
796 ast_log(LOG_WARNING, "Error getting conference\n");
801 /* Whoa, already in a conference... Retry... */
803 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
808 memset(&ztc, 0, sizeof(ztc));
809 /* Add us to the conference */
811 ztc.confno = conf->zapconf;
812 ast_mutex_lock(&conflock);
813 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER) && conf->users > 1) {
814 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
815 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
816 ast_waitstream(conf->chan, "");
817 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
818 ast_waitstream(conf->chan, "");
822 if (confflags & CONFFLAG_MONITOR)
823 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
824 else if (confflags & CONFFLAG_TALKER)
825 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
827 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
829 if (ioctl(fd, ZT_SETCONF, &ztc)) {
830 ast_log(LOG_WARNING, "Error setting conference\n");
832 ast_mutex_unlock(&conflock);
835 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
837 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
842 chan->name, chan->uniqueid, conf->confno, user->user_no);
844 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
846 if (!(confflags & CONFFLAG_QUIET))
847 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers >= 1)
848 conf_play(chan, conf, ENTER);
851 ast_mutex_unlock(&conflock);
852 if (confflags & CONFFLAG_AGI) {
854 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
855 or use default filename of conf-background.agi */
857 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
859 agifile = agifiledefault;
861 if (!strcasecmp(chan->type,"Zap")) {
862 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
864 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
866 /* Find a pointer to the agi app and execute the script */
867 app = pbx_findapp("agi");
869 ret = pbx_exec(chan, app, agifile, 1);
871 ast_log(LOG_WARNING, "Could not find application (agi)\n");
874 if (!strcasecmp(chan->type,"Zap")) {
875 /* Remove CONFMUTE mode on Zap channel */
877 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
880 if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
881 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
883 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
885 if (confflags & CONFFLAG_MONITORTALKER && !(dsp = ast_dsp_new())) {
886 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
892 currentmarked = conf->markedusers;
893 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_MARKEDUSER) && (confflags & CONFFLAG_WAITMARKED) && lastmarked == 0) {
894 if (currentmarked == 1 && conf->users > 1) {
895 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
896 if (conf->users - 1 == 1) {
897 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
898 ast_waitstream(chan, "");
900 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
901 ast_waitstream(chan, "");
904 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
905 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
906 ast_waitstream(chan, "");
909 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
911 /* Update the struct with the actual confflags */
912 user->userflags = confflags;
914 if (confflags & CONFFLAG_WAITMARKED) {
915 if(currentmarked == 0) {
916 if (lastmarked != 0) {
917 if (!(confflags & CONFFLAG_QUIET))
918 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
919 ast_waitstream(chan, "");
920 if(confflags & CONFFLAG_MARKEDEXIT)
923 ztc.confmode = ZT_CONF_CONF;
924 if (ioctl(fd, ZT_SETCONF, &ztc)) {
925 ast_log(LOG_WARNING, "Error setting conference\n");
931 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
932 ast_moh_start(chan, NULL);
935 ztc.confmode = ZT_CONF_CONF;
936 if (ioctl(fd, ZT_SETCONF, &ztc)) {
937 ast_log(LOG_WARNING, "Error setting conference\n");
942 } else if(currentmarked >= 1 && lastmarked == 0) {
943 if (confflags & CONFFLAG_MONITOR)
944 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
945 else if (confflags & CONFFLAG_TALKER)
946 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
948 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
949 if (ioctl(fd, ZT_SETCONF, &ztc)) {
950 ast_log(LOG_WARNING, "Error setting conference\n");
954 if (musiconhold && (confflags & CONFFLAG_MOH)) {
958 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
959 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
960 ast_waitstream(chan, "");
961 conf_play(chan, conf, ENTER);
966 /* trying to add moh for single person conf */
967 if (confflags & CONFFLAG_MOH) {
968 if (conf->users == 1) {
969 if (musiconhold == 0) {
970 ast_moh_start(chan, NULL);
981 /* Leave if the last marked user left */
982 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
987 /* Check if the admin changed my modes */
988 if (user->adminflags) {
989 /* Set the new modes */
990 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
991 ztc.confmode ^= ZT_CONF_TALKER;
992 if (ioctl(fd, ZT_SETCONF, &ztc)) {
993 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
998 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
999 ztc.confmode |= ZT_CONF_TALKER;
1000 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1001 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1006 if (user->adminflags & ADMINFLAG_KICKME) {
1007 /* You have been kicked. */
1008 if (!ast_streamfile(chan, "conf-kicked", chan->language))
1009 ast_waitstream(chan, "");
1013 } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1014 ztc.confmode |= ZT_CONF_TALKER;
1015 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1016 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1023 if (c->fds[0] != origfd) {
1025 /* Kill old pseudo */
1028 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
1036 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1037 if (confflags & CONFFLAG_MONITORTALKER) {
1039 if (user->talking == -1)
1042 res = ast_dsp_silence(dsp, f, &totalsilence);
1043 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1045 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1050 chan->name, chan->uniqueid, conf->confno, user->user_no);
1052 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1054 manager_event(EVENT_FLAG_CALL, "MeetmeStopTalking",
1059 chan->name, chan->uniqueid, conf->confno, user->user_no);
1063 /* Carefully write */
1064 careful_write(fd, f->data, f->datalen);
1066 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
1068 tmp[0] = f->subclass;
1070 if (ast_exists_extension(chan, exitcontext, tmp, 1, chan->cid.cid_num)) {
1071 strncpy(chan->context, exitcontext, sizeof(chan->context) - 1);
1072 strncpy(chan->exten, tmp, sizeof(chan->exten) - 1);
1077 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
1080 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
1081 int oldconfmode = 0;
1082 oldconfmode = ztc.confmode;
1084 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1085 ast_log(LOG_WARNING, "Error setting conference\n");
1087 ast_mutex_unlock(&conflock);
1093 if ((confflags & CONFFLAG_ADMIN)) {
1097 /* Record this sound! */
1098 if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
1099 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1106 case '1': /* Un/Mute */
1108 if (ztc.confmode & ZT_CONF_TALKER) {
1109 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1110 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1112 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1113 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1115 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1116 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1120 if (ztc.confmode & ZT_CONF_TALKER) {
1121 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1122 ast_waitstream(chan, "");
1124 if (!ast_streamfile(chan, "conf-muted", chan->language))
1125 ast_waitstream(chan, "");
1128 case '2': /* Un/Lock the Conference */
1132 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1133 ast_waitstream(chan, "");
1136 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1137 ast_waitstream(chan, "");
1140 case '3': /* Eject last user */
1142 usr = conf->lastuser;
1143 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1144 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1145 ast_waitstream(chan, "");
1147 usr->adminflags |= ADMINFLAG_KICKME;
1148 ast_stopstream(chan);
1152 /* Play an error message! */
1153 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1154 ast_waitstream(chan, "");
1162 /* Record this sound! */
1163 if (!ast_streamfile(chan, "conf-usermenu", chan->language))
1164 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1171 case '1': /* Un/Mute */
1173 if (ztc.confmode & ZT_CONF_TALKER) {
1174 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1175 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1176 } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
1177 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1178 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1180 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1181 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1185 if (ztc.confmode & ZT_CONF_TALKER) {
1186 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1187 ast_waitstream(chan, "");
1189 if (!ast_streamfile(chan, "conf-muted", chan->language))
1190 ast_waitstream(chan, "");
1195 /* Play an error message! */
1196 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1197 ast_waitstream(chan, "");
1203 ast_moh_start(chan, NULL);
1205 ztc.confmode = oldconfmode;
1206 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1207 ast_log(LOG_WARNING, "Error setting conference\n");
1209 ast_mutex_unlock(&conflock);
1213 } else if (option_debug) {
1214 ast_log(LOG_DEBUG, "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",chan->name,f->frametype,f->subclass);
1217 } else if (outfd > -1) {
1218 res = read(outfd, buf, CONF_SIZE);
1220 memset(&fr, 0, sizeof(fr));
1221 fr.frametype = AST_FRAME_VOICE;
1222 fr.subclass = AST_FORMAT_SLINEAR;
1226 fr.offset = AST_FRIENDLY_OFFSET;
1227 if (ast_write(chan, &fr) < 0) {
1228 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1232 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1234 lastmarked = currentmarked;
1240 /* Take out of conference */
1241 /* Add us to the conference */
1245 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1246 ast_log(LOG_WARNING, "Error setting conference\n");
1250 ast_mutex_lock(&conflock);
1251 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1252 conf_play(chan, conf, LEAVE);
1254 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
1255 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
1256 if ((conf->chan) && (conf->users > 1)) {
1257 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1258 ast_waitstream(conf->chan, "");
1259 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1260 ast_waitstream(conf->chan, "");
1262 ast_filedelete(user->namerecloc, NULL);
1265 ast_mutex_unlock(&conflock);
1269 ast_mutex_lock(&conflock);
1270 if (confflags & CONFFLAG_MONITORTALKER && dsp)
1273 if (user->user_no) { /* Only cleanup users who really joined! */
1274 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
1279 chan->name, chan->uniqueid, conf->confno, user->user_no);
1282 if (confflags & CONFFLAG_MARKEDUSER)
1283 conf->markedusers--;
1286 /* No more users -- close this one out */
1290 prev->next = conf->next;
1299 ast_log(LOG_WARNING, "Conference not found\n");
1300 if (conf->recording == MEETME_RECORD_ACTIVE) {
1301 conf->recording = MEETME_RECORD_TERMINATE;
1302 ast_mutex_unlock(&conflock);
1304 ast_mutex_lock(&conflock);
1305 if (conf->recording == MEETME_RECORD_OFF)
1307 ast_mutex_unlock(&conflock);
1311 ast_hangup(conf->chan);
1316 /* Remove the user struct */
1317 if (user == conf->firstuser) {
1318 if (user->nextuser) {
1319 /* There is another entry */
1320 user->nextuser->prevuser = NULL;
1322 /* We are the only entry */
1323 conf->lastuser = NULL;
1325 /* In either case */
1326 conf->firstuser = user->nextuser;
1327 } else if (user == conf->lastuser){
1329 user->prevuser->nextuser = NULL;
1331 ast_log(LOG_ERROR, "Bad bad bad! We're the last, not the first, but nobody before us??\n");
1332 conf->lastuser = user->prevuser;
1335 user->nextuser->prevuser = user->prevuser;
1337 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
1339 user->prevuser->nextuser = user->nextuser;
1341 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
1344 /* Return the number of seconds the user was in the conf */
1345 snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (time(NULL) - user->jointime));
1346 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1349 ast_mutex_unlock(&conflock);
1353 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
1355 struct ast_config *cfg;
1356 struct ast_variable *var;
1357 struct ast_conference *cnf;
1359 /* Check first in the conference list */
1360 ast_mutex_lock(&conflock);
1363 if (!strcmp(confno, cnf->confno))
1367 ast_mutex_unlock(&conflock);
1371 /* No need to parse meetme.conf */
1372 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1374 if (dynamic_pin[0] == 'q') {
1375 /* Query the user to enter a PIN */
1376 ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
1378 cnf = build_conf(confno, dynamic_pin, "", make, dynamic);
1380 cnf = build_conf(confno, "", "", make, dynamic);
1383 /* Check the config */
1384 cfg = ast_config_load("meetme.conf");
1386 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
1389 var = ast_variable_browse(cfg, "rooms");
1391 if (!strcasecmp(var->name, "conf")) {
1392 /* Separate the PIN */
1393 char *pin, *pinadmin, *conf;
1395 if ((pinadmin = ast_strdupa(var->value))) {
1396 conf = strsep(&pinadmin, "|,");
1397 pin = strsep(&pinadmin, "|,");
1398 if (!strcasecmp(conf, confno)) {
1399 /* Bingo it's a valid conference */
1402 cnf = build_conf(confno, pin, pinadmin, make, dynamic);
1404 cnf = build_conf(confno, pin, "", make, dynamic);
1407 cnf = build_conf(confno, "", pinadmin, make, dynamic);
1409 cnf = build_conf(confno, "", "", make, dynamic);
1417 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1419 ast_config_destroy(cfg);
1421 } else if (dynamic_pin) {
1422 /* Correct for the user selecting 'D' instead of 'd' to have
1423 someone join into a conference that has already been created
1425 if (dynamic_pin[0] == 'q')
1426 dynamic_pin[0] = '\0';
1431 /*--- count_exec: The MeetmeCount application */
1432 static int count_exec(struct ast_channel *chan, void *data)
1434 struct localuser *u;
1436 struct ast_conference *conf;
1438 char *confnum, *localdata;
1441 if (!data || ast_strlen_zero(data)) {
1442 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1445 localdata = ast_strdupa(data);
1447 confnum = strsep(&localdata,"|");
1448 conf = find_conf(chan, confnum, 0, 0, NULL);
1450 count = conf->users;
1454 if (localdata && !ast_strlen_zero(localdata)){
1455 /* have var so load it and exit */
1456 snprintf(val,sizeof(val), "%i",count);
1457 pbx_builtin_setvar_helper(chan, localdata,val);
1459 if (chan->_state != AST_STATE_UP)
1461 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1463 LOCAL_USER_REMOVE(u);
1467 /*--- conf_exec: The meetme() application */
1468 static int conf_exec(struct ast_channel *chan, void *data)
1471 struct localuser *u;
1472 char confno[AST_MAX_EXTENSION] = "";
1475 struct ast_conference *cnf;
1476 struct ast_flags confflags = {0};
1478 int empty = 0, empty_no_pin = 0;
1479 int always_prompt = 0;
1480 char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
1482 if (!data || ast_strlen_zero(data)) {
1489 if (chan->_state != AST_STATE_UP)
1492 info = ast_strdupa((char *)notdata);
1495 char *tmp = strsep(&info, "|");
1496 strncpy(confno, tmp, sizeof(confno) - 1);
1497 if (ast_strlen_zero(confno)) {
1502 inflags = strsep(&info, "|");
1504 inpin = strsep(&info, "|");
1506 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
1509 ast_parseoptions(meetme_opts, &confflags, NULL, inflags);
1510 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
1511 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !inpin)
1512 strncpy(the_pin, "q", sizeof(the_pin) - 1);
1514 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
1515 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
1516 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
1524 struct ast_config *cfg;
1525 struct ast_variable *var;
1528 memset(map, 0, sizeof(map));
1530 ast_mutex_lock(&conflock);
1533 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
1534 /* Disqualify in use conference */
1535 if (confno_int >= 0 && confno_int < 1024)
1540 ast_mutex_unlock(&conflock);
1542 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
1543 if ((empty_no_pin) || (!dynamic)) {
1544 cfg = ast_config_load("meetme.conf");
1546 var = ast_variable_browse(cfg, "rooms");
1548 if (!strcasecmp(var->name, "conf")) {
1549 char *stringp = ast_strdupa(var->value);
1551 char *confno_tmp = strsep(&stringp, "|,");
1553 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
1554 if ((confno_int >= 0) && (confno_int < 1024)) {
1555 if (stringp && empty_no_pin) {
1561 /* For static: run through the list and see if this conference is empty */
1562 ast_mutex_lock(&conflock);
1565 if (!strcmp(confno_tmp, cnf->confno)) {
1566 /* The conference exists, therefore it's not empty */
1572 ast_mutex_unlock(&conflock);
1574 /* At this point, we have a confno_tmp (static conference) that is empty */
1575 if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) {
1576 /* Case 1: empty_no_pin and pin is nonexistant (NULL)
1577 * Case 2: empty_no_pin and pin is blank (but not NULL)
1578 * Case 3: not empty_no_pin
1580 strncpy(confno, confno_tmp, sizeof(confno) - 1);
1582 /* XXX the map is not complete (but we do have a confno) */
1587 ast_log(LOG_ERROR, "Out of memory\n");
1592 ast_config_destroy(cfg);
1595 /* Select first conference number not in use */
1596 if (ast_strlen_zero(confno) && dynamic) {
1597 for (i=0;i<1024;i++) {
1599 snprintf(confno, sizeof(confno), "%d", i);
1606 if (ast_strlen_zero(confno)) {
1607 res = ast_streamfile(chan, "conf-noempty", chan->language);
1609 ast_waitstream(chan, "");
1611 if (sscanf(confno, "%d", &confno_int) == 1) {
1612 res = ast_streamfile(chan, "conf-enteringno", chan->language);
1614 ast_waitstream(chan, "");
1615 res = ast_say_digits(chan, confno_int, "", chan->language);
1618 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
1622 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
1623 /* Prompt user for conference number */
1624 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
1626 /* Don't try to validate when we catch an error */
1632 if (!ast_strlen_zero(confno)) {
1633 /* Check the validity of the conference */
1634 cnf = find_conf(chan, confno, 1, dynamic, the_pin);
1636 res = ast_streamfile(chan, "conf-invalid", chan->language);
1638 ast_waitstream(chan, "");
1643 if ((!ast_strlen_zero(cnf->pin) && !ast_test_flag(&confflags, CONFFLAG_ADMIN)) || (!ast_strlen_zero(cnf->pinadmin) && ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
1644 char pin[AST_MAX_EXTENSION]="";
1647 /* Allow the pin to be retried up to 3 times */
1648 for (j=0; j<3; j++) {
1649 if (*the_pin && (always_prompt==0)) {
1650 strncpy(pin, the_pin, sizeof(pin) - 1);
1653 /* Prompt user for pin if pin is required */
1654 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
1657 if (!strcasecmp(pin, cnf->pin) || (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))) {
1661 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
1662 ast_set_flag(&confflags, CONFFLAG_ADMIN);
1663 /* Run the conference */
1664 res = conf_run(chan, cnf, confflags.flags);
1668 res = ast_streamfile(chan, "conf-invalidpin", chan->language);
1670 ast_waitstream(chan, AST_DIGIT_ANY);
1685 /* Don't retry pin with a static pin */
1686 if (*the_pin && (always_prompt==0)) {
1691 /* No pin required */
1694 /* Run the conference */
1695 res = conf_run(chan, cnf, confflags.flags);
1699 } while (allowretry);
1700 /* Do the conference */
1701 LOCAL_USER_REMOVE(u);
1705 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
1706 struct ast_conf_user *user = NULL;
1707 char usrno[1024] = "";
1708 if (conf && callerident) {
1709 user = conf->firstuser;
1711 snprintf(usrno, sizeof(usrno), "%i", user->user_no);
1712 if (strcmp(usrno, callerident) == 0)
1714 user = user->nextuser;
1720 /*--- admin_exec: The MeetMeadmin application */
1721 /* MeetMeAdmin(confno, command, caller) */
1722 static int admin_exec(struct ast_channel *chan, void *data) {
1723 char *params, *command = NULL, *caller = NULL, *conf = NULL;
1724 struct ast_conference *cnf;
1725 struct ast_conf_user *user = NULL;
1727 ast_mutex_lock(&conflock);
1728 /* The param has the conference number the user and the command to execute */
1729 if (data && !ast_strlen_zero(data)) {
1730 params = ast_strdupa((char *) data);
1731 conf = strsep(¶ms, "|");
1732 command = strsep(¶ms, "|");
1733 caller = strsep(¶ms, "|");
1736 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
1737 ast_mutex_unlock(&conflock);
1742 if (strcmp(cnf->confno, conf) == 0)
1748 user = find_user(cnf, caller);
1751 switch((int) (*command)) {
1752 case 76: /* L: Lock */
1755 case 108: /* l: Unlock */
1758 case 75: /* K: kick all users*/
1759 user = cnf->firstuser;
1761 user->adminflags |= ADMINFLAG_KICKME;
1762 if (user->nextuser) {
1763 user = user->nextuser;
1769 case 101: /* e: Eject last user*/
1770 user = cnf->lastuser;
1771 if (!(user->userflags & CONFFLAG_ADMIN)) {
1772 user->adminflags |= ADMINFLAG_KICKME;
1775 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
1777 case 77: /* M: Mute */
1779 user->adminflags |= ADMINFLAG_MUTED;
1781 ast_log(LOG_NOTICE, "Specified User not found!\n");
1784 case 78: /* N: Mute all users */
1785 user = cnf->firstuser;
1787 if (user && !(user->userflags & CONFFLAG_ADMIN))
1788 user->adminflags |= ADMINFLAG_MUTED;
1789 if (user->nextuser) {
1790 user = user->nextuser;
1796 case 109: /* m: Unmute */
1797 if (user && (user->adminflags & ADMINFLAG_MUTED)) {
1798 user->adminflags ^= ADMINFLAG_MUTED;
1800 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
1803 case 110: /* n: Unmute all users */
1804 user = cnf->firstuser;
1806 if (user && (user-> adminflags & ADMINFLAG_MUTED)) {
1807 user->adminflags ^= ADMINFLAG_MUTED;
1809 if (user->nextuser) {
1810 user = user->nextuser;
1816 case 107: /* k: Kick user */
1818 user->adminflags |= ADMINFLAG_KICKME;
1820 ast_log(LOG_NOTICE, "Specified User not found!");
1825 ast_log(LOG_NOTICE, "Conference Number not found\n");
1828 ast_mutex_unlock(&conflock);
1832 static void *recordthread(void *args)
1834 struct ast_conference *cnf;
1835 struct ast_frame *f=NULL;
1837 struct ast_filestream *s;
1840 cnf = (struct ast_conference *)args;
1841 if( !cnf || !cnf->chan ) {
1844 ast_stopstream(cnf->chan);
1845 flags = O_CREAT|O_TRUNC|O_WRONLY;
1846 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
1849 cnf->recording = MEETME_RECORD_ACTIVE;
1850 while (ast_waitfor(cnf->chan, -1) > -1) {
1851 f = ast_read(cnf->chan);
1856 if (f->frametype == AST_FRAME_VOICE) {
1857 res = ast_writestream(s, f);
1862 if (cnf->recording == MEETME_RECORD_TERMINATE) {
1863 ast_mutex_lock(&conflock);
1864 ast_mutex_unlock(&conflock);
1868 cnf->recording = MEETME_RECORD_OFF;
1874 int unload_module(void)
1876 STANDARD_HANGUP_LOCALUSERS;
1877 ast_cli_unregister(&cli_show_confs);
1878 ast_cli_unregister(&cli_conf);
1879 ast_unregister_application(app3);
1880 ast_unregister_application(app2);
1881 return ast_unregister_application(app);
1884 int load_module(void)
1886 ast_cli_register(&cli_show_confs);
1887 ast_cli_register(&cli_conf);
1888 ast_register_application(app3, admin_exec, synopsis3, descrip3);
1889 ast_register_application(app2, count_exec, synopsis2, descrip2);
1890 return ast_register_application(app, conf_exec, synopsis, descrip);
1894 char *description(void)
1902 STANDARD_USECOUNT(res);
1908 return ASTERISK_GPL_KEY;