2 * Asterisk -- A telephony toolkit for Linux.
4 * Meet me conference bridge
6 * Copyright (C) 1999-2004, 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/musiconhold.h>
23 #include <asterisk/manager.h>
24 #include <asterisk/options.h>
25 #include <asterisk/cli.h>
26 #include <asterisk/say.h>
27 #include <asterisk/utils.h>
33 #include <sys/ioctl.h>
36 #include <linux/zaptel.h>
39 #endif /* __linux__ */
41 static char *tdesc = "MeetMe conference bridge";
43 static char *app = "MeetMe";
44 static char *app2 = "MeetMeCount";
45 static char *app3 = "MeetMeAdmin";
47 static char *synopsis = "MeetMe conference bridge";
48 static char *synopsis2 = "MeetMe participant count";
49 static char *synopsis3 = "MeetMe conference Administration";
51 static char *descrip =
52 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe conference.\n"
53 "If the conference number is omitted, the user will be prompted to enter\n"
55 "MeetMe returns 0 if user pressed # to exit (see option 'p'), otherwise -1.\n"
56 "Please note: A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING TO WORK!\n\n"
58 "The option string may contain zero or more of the following characters:\n"
59 " 'm' -- set monitor only mode (Listen only, no talking)\n"
60 " 't' -- set talk only mode. (Talk only, no listening)\n"
61 " 'p' -- allow user to exit the conference by pressing '#'\n"
62 " 'X' -- allow user to exit the conference by entering a valid single\n"
63 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
64 " if that variable is not defined.\n"
65 " 'd' -- dynamically add conference\n"
66 " 'D' -- dynamically add conference, prompting for a PIN\n"
67 " 'e' -- select an empty conference\n"
68 " 'E' -- select an empty pinless conference\n"
69 " 'v' -- video mode\n"
70 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
71 " 'M' -- enable music on hold when the conference has a single caller\n"
72 " 'x' -- close the conference when last marked user exits\n"
73 " 'w' -- wait until the marked user enters the conference\n"
74 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
75 " Default: conf-background.agi\n"
76 " (Note: This does not work with non-Zap channels in the same conference)\n"
77 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
78 " 'a' -- set admin mode\n"
79 " 'A' -- set marked mode\n";
81 static char *descrip2 =
82 " MeetMeCount(confno[|var]): Plays back the number of users in the specifiedi\n"
83 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
84 "will be returned in the variable. Returns 0 on success or -1 on a hangup.\n"
85 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
87 static char *descrip3 =
88 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
89 " 'K' -- Kick all users out of conference\n"
90 " 'k' -- Kick one user out of conference\n"
91 " 'L' -- Lock conference\n"
92 " 'l' -- Unlock conference\n"
93 " 'M' -- Mute conference\n"
94 " 'm' -- Unmute conference\n"
101 static struct ast_conference {
102 char confno[AST_MAX_EXTENSION]; /* Conference */
103 struct ast_channel *chan; /* Announcements channel */
104 int fd; /* Announcements fd */
105 int zapconf; /* Zaptel Conf # */
106 int users; /* Number of active users */
107 int markedusers; /* Number of marked users */
108 struct ast_conf_user *firstuser; /* Pointer to the first user struct */
109 struct ast_conf_user *lastuser; /* Pointer to the last user struct */
110 time_t start; /* Start time (s) */
111 int isdynamic; /* Created on the fly? */
112 int locked; /* Is the conference locked? */
113 char pin[AST_MAX_EXTENSION]; /* If protected by a PIN */
114 struct ast_conference *next;
117 struct ast_conf_user {
118 int user_no; /* User Number */
119 struct ast_conf_user *prevuser; /* Pointer to the previous user */
120 struct ast_conf_user *nextuser; /* Pointer to the next user */
121 int userflags; /* Flags as set in the conference */
122 int adminflags; /* Flags set by the Admin */
123 struct ast_channel *chan; /* Connected channel */
124 char usrvalue[50]; /* Custom User Value */
125 time_t jointime; /* Time the user joined the conference */
128 #define ADMINFLAG_MUTED (1 << 1) /* User is muted */
129 #define ADMINFLAG_KICKME (1 << 2) /* User is kicked */
132 AST_MUTEX_DEFINE_STATIC(conflock);
134 static int admin_exec(struct ast_channel *chan, void *data);
142 #define CONF_SIZE 320
144 #define CONFFLAG_ADMIN (1 << 1) /* If set the user has admin access on the conference */
145 #define CONFFLAG_MONITOR (1 << 2) /* If set the user can only receive audio from the conference */
146 #define CONFFLAG_POUNDEXIT (1 << 3) /* If set asterisk will exit conference when '#' is pressed */
147 #define CONFFLAG_STARMENU (1 << 4) /* If set asterisk will provide a menu to the user what '*' is pressed */
148 #define CONFFLAG_TALKER (1 << 5) /* If set the use can only send audio to the conference */
149 #define CONFFLAG_QUIET (1 << 6) /* If set there will be no enter or leave sounds */
150 #define CONFFLAG_VIDEO (1 << 7) /* Set to enable video mode */
151 #define CONFFLAG_AGI (1 << 8) /* Set to run AGI Script in Background */
152 #define CONFFLAG_MOH (1 << 9) /* Set to have music on hold when user is alone in conference */
153 #define CONFFLAG_MARKEDEXIT (1 << 10) /* If set the MeetMe will return if all marked with this flag left */
154 #define CONFFLAG_WAITMARKED (1 << 11) /* If set, the MeetMe will wait until a marked user enters */
155 #define CONFFLAG_EXIT_CONTEXT (1 << 12) /* If set, the MeetMe will exit to the specified context */
156 #define CONFFLAG_MARKEDUSER (1 << 13) /* If set, the user will be marked */
159 static int careful_write(int fd, unsigned char *data, int len)
164 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
165 res = ioctl(fd, ZT_IOMUX, &x);
167 res = write(fd, data, len);
169 if (errno != EAGAIN) {
170 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
181 static void conf_play(struct ast_conference *conf, int sound)
185 ast_mutex_lock(&conflock);
200 careful_write(conf->fd, data, len);
201 ast_mutex_unlock(&conflock);
204 static struct ast_conference *build_conf(char *confno, char *pin, int make, int dynamic)
206 struct ast_conference *cnf;
207 struct zt_confinfo ztc;
208 ast_mutex_lock(&conflock);
211 if (!strcmp(confno, cnf->confno))
215 if (!cnf && (make || dynamic)) {
216 cnf = malloc(sizeof(struct ast_conference));
219 memset(cnf, 0, sizeof(struct ast_conference));
220 strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1);
221 strncpy(cnf->pin, pin, sizeof(cnf->pin) - 1);
222 cnf->markedusers = 0;
223 cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo");
225 cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */
227 ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
228 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
230 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
236 memset(&ztc, 0, sizeof(ztc));
237 /* Setup a new zap conference */
240 ztc.confmode = ZT_CONF_CONFANN;
241 if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
242 ast_log(LOG_WARNING, "Error setting conference\n");
244 ast_hangup(cnf->chan);
251 /* Fill the conference struct */
252 cnf->start = time(NULL);
253 cnf->zapconf = ztc.confno;
254 cnf->isdynamic = dynamic;
255 cnf->firstuser = NULL;
256 cnf->lastuser = NULL;
258 if (option_verbose > 2)
259 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
263 ast_log(LOG_WARNING, "Out of memory\n");
266 ast_mutex_unlock(&conflock);
270 static int confs_show(int fd, int argc, char **argv)
272 ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
273 return RESULT_SUCCESS;
276 static char show_confs_usage[] =
277 "Deprecated! Please use 'meetme' instead.\n";
279 static struct ast_cli_entry cli_show_confs = {
280 { "show", "conferences", NULL }, confs_show,
281 "Show status of conferences", show_confs_usage, NULL };
283 static int conf_cmd(int fd, int argc, char **argv) {
284 /* Process the command */
285 struct ast_conference *cnf;
286 struct ast_conf_user *user;
288 int i = 0, total = 0;
290 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
291 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
292 char cmdline[1024] = "";
295 ast_cli(fd, "Invalid Arguments.\n");
296 /* Check for length so no buffer will overflow... */
297 for (i = 0; i < argc; i++) {
298 if (strlen(argv[i]) > 100)
299 ast_cli(fd, "Invalid Arguments.\n");
302 /* 'MeetMe': List all the conferences */
306 ast_cli(fd, "No active MeetMe conferences.\n");
307 return RESULT_SUCCESS;
309 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
311 if (cnf->markedusers == 0)
312 strncpy(cmdline, "N/A ", sizeof(cmdline) - 1);
314 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
315 hr = (now - cnf->start) / 3600;
316 min = ((now - cnf->start) % 3600) / 60;
317 sec = (now - cnf->start) % 60;
319 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
324 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
325 return RESULT_SUCCESS;
328 return RESULT_SHOWUSAGE;
329 strncpy(cmdline, argv[2], sizeof(cmdline) - 1); /* Argv 2: conference number */
330 if (strstr(argv[1], "lock")) {
331 if (strcmp(argv[1], "lock") == 0) {
333 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
336 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
338 } else if (strstr(argv[1], "mute")) {
340 return RESULT_SHOWUSAGE;
341 if (strcmp(argv[1], "mute") == 0) {
343 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
344 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
347 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
348 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
350 } else if (strcmp(argv[1], "kick") == 0) {
352 return RESULT_SHOWUSAGE;
353 if (strcmp(argv[3], "all") == 0) {
355 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
357 /* Kick a single user */
358 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
359 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
361 } else if(strcmp(argv[1], "list") == 0) {
362 /* List all the users in a conference */
364 ast_cli(fd, "No active conferences.\n");
365 return RESULT_SUCCESS;
368 /* Find the right conference */
370 if (strcmp(cnf->confno, argv[2]) == 0)
375 ast_cli(fd, "No such conference: %s.\n",argv[2]);
376 return RESULT_SUCCESS;
379 /* Show all the users */
380 user = cnf->firstuser;
382 ast_cli(fd, "User #: %i Channel: %s %s %s\n", user->user_no, user->chan->name, (user->userflags & CONFFLAG_ADMIN) ? "(Admin)" : "", (user->userflags & CONFFLAG_MONITOR) ? "(Listen only)" : "" );
383 user = user->nextuser;
385 return RESULT_SUCCESS;
387 return RESULT_SHOWUSAGE;
388 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
389 admin_exec(NULL, cmdline);
393 static char *complete_confcmd(char *line, char *word, int pos, int state) {
394 #define CONF_COMMANDS 6
395 int which = 0, x = 0;
396 struct ast_conference *cnf = NULL;
397 struct ast_conf_user *usr = NULL;
400 char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"};
405 for (x = 0;x < CONF_COMMANDS; x++) {
406 if (!strncasecmp(cmds[x], word, strlen(word))) {
407 if (++which > state) {
408 return strdup(cmds[x]);
412 } else if (pos == 2) {
413 /* Conference Number */
414 ast_mutex_lock(&conflock);
417 if (!strncasecmp(word, cnf->confno, strlen(word))) {
423 ast_mutex_unlock(&conflock);
424 return cnf ? strdup(cnf->confno) : NULL;
425 } else if (pos == 3) {
426 /* User Number || Conf Command option*/
427 if (strstr(line, "mute") || strstr(line, "kick")) {
428 if ((state == 0) && (strstr(line, "kick")) && !(strncasecmp(word, "all", strlen(word)))) {
429 return strdup("all");
432 ast_mutex_lock(&conflock);
435 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
436 myline = ast_strdupa(line);
437 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
438 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
443 if (strcmp(confno, cnf->confno) == 0) {
449 /* Search for the user */
450 usr = cnf->firstuser;
452 snprintf(usrno, sizeof(usrno), "%i", usr->user_no);
453 if (!strncasecmp(word, usrno, strlen(word))) {
460 ast_mutex_unlock(&conflock);
461 return usr ? strdup(usrno) : NULL;
467 static char conf_usage[] =
468 "Usage: meetme (un)lock|(un)mute|kick|list <confno> <usernumber>\n"
469 " Executes a command for the conference or on a conferee\n";
471 static struct ast_cli_entry cli_conf = {
472 { "meetme", NULL, NULL }, conf_cmd,
473 "Execute a command on a conference or conferee", conf_usage, complete_confcmd };
475 static int confnonzero(void *ptr)
477 struct ast_conference *conf = ptr;
479 ast_mutex_lock(&conflock);
480 res = (conf->markedusers == 0);
481 ast_mutex_unlock(&conflock);
485 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
487 struct ast_conference *prev=NULL, *cur;
488 struct ast_conf_user *user = malloc(sizeof(struct ast_conf_user));
490 struct zt_confinfo ztc;
492 struct ast_channel *c;
507 int using_pseudo = 0;
511 char *agifiledefault = "conf-background.agi";
512 char meetmesecs[30] = "";
513 char exitcontext[AST_MAX_EXTENSION] = "";
517 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
518 char *buf = __buf + AST_FRIENDLY_OFFSET;
521 ast_log(LOG_ERROR, "Out of memory\n");
524 memset(user, 0, sizeof(struct ast_conf_user));
526 user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */
529 /* Sorry, but this confernce is locked! */
530 if (!ast_streamfile(chan, "conf-locked", chan->language))
531 ast_waitstream(chan, "");
535 if (confflags & CONFFLAG_MARKEDUSER)
538 ast_mutex_lock(&conflock);
539 if (conf->firstuser == NULL) {
540 /* Fill the first new User struct */
542 user->nextuser = NULL;
543 user->prevuser = NULL;
544 conf->firstuser = user;
545 conf->lastuser = user;
547 /* Fill the new user struct */
548 user->user_no = conf->lastuser->user_no + 1;
549 user->prevuser = conf->lastuser;
550 user->nextuser = NULL;
551 if (conf->lastuser->nextuser != NULL) {
552 ast_log(LOG_WARNING, "Error in User Management!\n");
553 ast_mutex_unlock(&conflock);
556 conf->lastuser->nextuser = user;
557 conf->lastuser = user;
560 strncpy(user->usrvalue, "test", sizeof(user->usrvalue) - 1);
562 user->userflags = confflags;
563 user->adminflags = 0;
564 ast_mutex_unlock(&conflock);
565 origquiet = confflags & CONFFLAG_QUIET;
566 if (confflags & CONFFLAG_EXIT_CONTEXT) {
567 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
568 strncpy(exitcontext, agifile, sizeof(exitcontext) - 1);
569 else if (!ast_strlen_zero(chan->macrocontext))
570 strncpy(exitcontext, chan->macrocontext, sizeof(exitcontext) - 1);
572 strncpy(exitcontext, chan->context, sizeof(exitcontext) - 1);
574 while((confflags & CONFFLAG_WAITMARKED) && (conf->markedusers == 0)) {
575 confflags &= ~CONFFLAG_QUIET;
576 confflags |= origquiet;
577 /* XXX Announce that we're waiting on the conference lead to join */
578 if (!(confflags & CONFFLAG_QUIET)) {
579 res = ast_streamfile(chan, "vm-dialout", chan->language);
581 res = ast_waitstream(chan, "");
584 /* If we're waiting with hold music, set to silent mode */
586 confflags |= CONFFLAG_QUIET;
587 ast_moh_start(chan, NULL);
588 res = ast_safe_sleep_conditional(chan, 60000, confnonzero, conf);
592 ast_log(LOG_DEBUG, "Got hangup on '%s' already\n", chan->name);
597 if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
598 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
599 if (ast_waitstream(chan, "") < 0)
605 /* Set it into linear mode (write) */
606 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
607 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
611 /* Set it into linear mode (read) */
612 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
613 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
616 ast_indicate(chan, -1);
617 retryzap = strcasecmp(chan->type, "Zap");
619 origfd = chan->fds[0];
621 fd = open("/dev/zap/pseudo", O_RDWR);
623 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
627 /* Make non-blocking */
628 flags = fcntl(fd, F_GETFL);
630 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
634 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
635 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
639 /* Setup buffering information */
640 memset(&bi, 0, sizeof(bi));
641 bi.bufsize = CONF_SIZE/2;
642 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
643 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
645 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
646 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
651 if (ioctl(fd, ZT_SETLINEAR, &x)) {
652 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
658 /* XXX Make sure we're not running on a pseudo channel XXX */
662 memset(&ztc, 0, sizeof(ztc));
663 /* Check to see if we're in a conference... */
665 if (ioctl(fd, ZT_GETCONF, &ztc)) {
666 ast_log(LOG_WARNING, "Error getting conference\n");
671 /* Whoa, already in a conference... Retry... */
673 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
678 memset(&ztc, 0, sizeof(ztc));
679 /* Add us to the conference */
681 ztc.confno = conf->zapconf;
682 if (confflags & CONFFLAG_MONITOR)
683 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
684 else if (confflags & CONFFLAG_TALKER)
685 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
687 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
689 if (ioctl(fd, ZT_SETCONF, &ztc)) {
690 ast_log(LOG_WARNING, "Error setting conference\n");
694 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
696 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
701 chan->name, chan->uniqueid, conf->confno, user->user_no);
703 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
705 if (!(confflags & CONFFLAG_QUIET))
706 conf_play(conf, ENTER);
709 if (confflags & CONFFLAG_AGI) {
711 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
712 or use default filename of conf-background.agi */
714 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
716 agifile = agifiledefault;
718 if (!strcasecmp(chan->type,"Zap")) {
719 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
721 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
723 /* Find a pointer to the agi app and execute the script */
724 app = pbx_findapp("agi");
726 ret = pbx_exec(chan, app, agifile, 1);
728 ast_log(LOG_WARNING, "Could not find application (agi)\n");
731 if (!strcasecmp(chan->type,"Zap")) {
732 /* Remove CONFMUTE mode on Zap channel */
734 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
737 if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
738 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
740 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
745 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
747 /* Update the struct with the actual confflags */
748 user->userflags = confflags;
750 /* trying to add moh for single person conf */
751 if (confflags & CONFFLAG_MOH) {
752 if (conf->users == 1) {
753 if (musiconhold == 0) {
754 ast_moh_start(chan, NULL);
765 /* Leave if the last marked user left */
766 if (conf->markedusers == 0 && confflags & CONFFLAG_MARKEDEXIT) {
771 /* Check if the admin changed my modes */
772 if (user->adminflags) {
773 /* Set the new modes */
774 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
775 ztc.confmode ^= ZT_CONF_TALKER;
776 if (ioctl(fd, ZT_SETCONF, &ztc)) {
777 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
782 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
783 ztc.confmode |= ZT_CONF_TALKER;
784 if (ioctl(fd, ZT_SETCONF, &ztc)) {
785 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
790 if (user->adminflags & ADMINFLAG_KICKME) {
791 //You have been kicked.
792 if (!ast_streamfile(chan, "conf-kicked", chan->language))
793 ast_waitstream(chan, "");
797 } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
798 ztc.confmode |= ZT_CONF_TALKER;
799 if (ioctl(fd, ZT_SETCONF, &ztc)) {
800 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
807 if (c->fds[0] != origfd) {
809 /* Kill old pseudo */
812 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
820 if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
822 tmp[0] = f->subclass;
824 if (ast_exists_extension(chan, exitcontext, tmp, 1, chan->callerid)) {
825 strncpy(chan->context, exitcontext, sizeof(chan->context) - 1);
826 strncpy(chan->exten, tmp, sizeof(chan->exten) - 1);
831 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
834 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
838 if ((confflags & CONFFLAG_ADMIN)) {
842 /* Record this sound! */
843 if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
844 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
851 case '1': /* Un/Mute */
853 if (ztc.confmode & ZT_CONF_TALKER) {
854 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
855 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
857 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
858 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
860 if (ioctl(fd, ZT_SETCONF, &ztc)) {
861 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
865 if (ztc.confmode & ZT_CONF_TALKER) {
866 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
867 ast_waitstream(chan, "");
869 if (!ast_streamfile(chan, "conf-muted", chan->language))
870 ast_waitstream(chan, "");
873 case '2': /* Un/Lock the Conference */
877 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
878 ast_waitstream(chan, "");
881 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
882 ast_waitstream(chan, "");
887 /* Play an error message! */
888 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
889 ast_waitstream(chan, "");
897 /* Record this sound! */
898 if (!ast_streamfile(chan, "conf-usermenu", chan->language))
899 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
906 case '1': /* Un/Mute */
908 if (ztc.confmode & ZT_CONF_TALKER) {
909 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
910 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
911 } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
912 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
913 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
915 if (ioctl(fd, ZT_SETCONF, &ztc)) {
916 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
920 if (ztc.confmode & ZT_CONF_TALKER) {
921 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
922 ast_waitstream(chan, "");
924 if (!ast_streamfile(chan, "conf-muted", chan->language))
925 ast_waitstream(chan, "");
930 /* Play an error message! */
931 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
932 ast_waitstream(chan, "");
938 ast_moh_start(chan, NULL);
940 } else if (using_pseudo) {
941 if (f->frametype == AST_FRAME_VOICE) {
942 if (f->subclass == AST_FORMAT_SLINEAR) {
943 /* Carefully write */
944 careful_write(fd, f->data, f->datalen);
946 ast_log(LOG_WARNING, "Huh? Got a non-linear (%d) frame in the conference\n", f->subclass);
950 } else if (outfd > -1) {
951 res = read(outfd, buf, CONF_SIZE);
953 memset(&fr, 0, sizeof(fr));
954 fr.frametype = AST_FRAME_VOICE;
955 fr.subclass = AST_FORMAT_SLINEAR;
959 fr.offset = AST_FRIENDLY_OFFSET;
960 if (ast_write(chan, &fr) < 0) {
961 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
965 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
972 /* Take out of conference */
973 /* Add us to the conference */
977 if (ioctl(fd, ZT_SETCONF, &ztc)) {
978 ast_log(LOG_WARNING, "Error setting conference\n");
981 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
982 conf_play(conf, LEAVE);
985 ast_mutex_lock(&conflock);
986 if (user->user_no) { /* Only cleanup users who really joined! */
987 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
992 chan->name, chan->uniqueid, conf->confno, user->user_no);
995 if (confflags & CONFFLAG_MARKEDUSER)
999 /* No more users -- close this one out */
1003 prev->next = conf->next;
1012 ast_log(LOG_WARNING, "Conference not found\n");
1014 ast_hangup(conf->chan);
1019 /* Remove the user struct */
1020 if (user == conf->firstuser) {
1021 if (user->nextuser) {
1022 /* There is another entry */
1023 user->nextuser->prevuser = NULL;
1025 /* We are the only entry */
1026 conf->lastuser = NULL;
1028 /* In either case */
1029 conf->firstuser = user->nextuser;
1030 } else if (user == conf->lastuser){
1032 user->prevuser->nextuser = NULL;
1034 ast_log(LOG_ERROR, "Bad bad bad! We're the last, not the first, but nobody before us??\n");
1035 conf->lastuser = user->prevuser;
1038 user->nextuser->prevuser = user->prevuser;
1040 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
1042 user->prevuser->nextuser = user->nextuser;
1044 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
1046 /* Return the number of seconds the user was in the conf */
1047 snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (user->jointime - time(NULL)));
1048 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1052 ast_mutex_unlock(&conflock);
1056 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
1058 struct ast_config *cfg;
1059 struct ast_variable *var;
1060 struct ast_conference *cnf;
1062 /* Check first in the conference list */
1063 ast_mutex_lock(&conflock);
1066 if (!strcmp(confno, cnf->confno))
1070 ast_mutex_unlock(&conflock);
1074 /* No need to parse meetme.conf */
1075 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1077 if (dynamic_pin[0] == 'q') {
1078 /* Query the user to enter a PIN */
1079 ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
1081 cnf = build_conf(confno, dynamic_pin, make, dynamic);
1083 cnf = build_conf(confno, "", make, dynamic);
1086 /* Check the config */
1087 cfg = ast_load("meetme.conf");
1089 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
1092 var = ast_variable_browse(cfg, "rooms");
1094 if (!strcasecmp(var->name, "conf")) {
1095 /* Separate the PIN */
1098 if ((pin = ast_strdupa(var->value))) {
1099 conf = strsep(&pin, "|,");
1100 if (!strcasecmp(conf, confno)) {
1101 /* Bingo it's a valid conference */
1103 cnf = build_conf(confno, pin, make, dynamic);
1105 cnf = build_conf(confno, "", make, dynamic);
1113 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1117 } else if (dynamic_pin) {
1118 /* Correct for the user selecting 'D' instead of 'd' to have
1119 someone join into a conference that has already been created
1121 if (dynamic_pin[0] == 'q')
1122 dynamic_pin[0] = '\0';
1127 /*--- count_exec: The MeetmeCount application */
1128 static int count_exec(struct ast_channel *chan, void *data)
1130 struct localuser *u;
1132 struct ast_conference *conf;
1134 char *confnum, *localdata;
1137 if (!data || ast_strlen_zero(data)) {
1138 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1141 localdata = ast_strdupa(data);
1143 confnum = strsep(&localdata,"|");
1144 conf = find_conf(chan, confnum, 0, 0, NULL);
1146 count = conf->users;
1150 if (localdata && !ast_strlen_zero(localdata)){
1151 /* have var so load it and exit */
1152 snprintf(val,sizeof(val), "%i",count);
1153 pbx_builtin_setvar_helper(chan, localdata,val);
1155 if (chan->_state != AST_STATE_UP)
1157 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1159 LOCAL_USER_REMOVE(u);
1163 /*--- conf_exec: The meetme() application */
1164 static int conf_exec(struct ast_channel *chan, void *data)
1167 struct localuser *u;
1168 char confno[AST_MAX_EXTENSION] = "";
1171 struct ast_conference *cnf;
1174 int empty = 0, empty_no_pin = 0;
1175 char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
1177 if (!data || ast_strlen_zero(data)) {
1184 if (chan->_state != AST_STATE_UP)
1187 info = ast_strdupa((char *)notdata);
1190 char *tmp = strsep(&info, "|");
1191 strncpy(confno, tmp, sizeof(confno) - 1);
1192 if (ast_strlen_zero(confno)) {
1197 inflags = strsep(&info, "|");
1199 inpin = strsep(&info, "|");
1201 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
1204 if (strchr(inflags, 'a'))
1205 confflags |= CONFFLAG_ADMIN;
1206 if (strchr(inflags, 'm'))
1207 confflags |= CONFFLAG_MONITOR;
1208 if (strchr(inflags, 'p'))
1209 confflags |= CONFFLAG_POUNDEXIT;
1210 if (strchr(inflags, 's'))
1211 confflags |= CONFFLAG_STARMENU;
1212 if (strchr(inflags, 't'))
1213 confflags |= CONFFLAG_TALKER;
1214 if (strchr(inflags, 'q'))
1215 confflags |= CONFFLAG_QUIET;
1216 if (strchr(inflags, 'M'))
1217 confflags |= CONFFLAG_MOH;
1218 if (strchr(inflags, 'x'))
1219 confflags |= CONFFLAG_MARKEDEXIT;
1220 if (strchr(inflags, 'X'))
1221 confflags |= CONFFLAG_EXIT_CONTEXT;
1222 if (strchr(inflags, 'A'))
1223 confflags |= CONFFLAG_MARKEDUSER;
1224 if (strchr(inflags, 'b'))
1225 confflags |= CONFFLAG_AGI;
1226 if (strchr(inflags, 'w'))
1227 confflags |= CONFFLAG_WAITMARKED;
1228 if (strchr(inflags, 'd'))
1230 if (strchr(inflags, 'D')) {
1233 strncpy(the_pin, "q", sizeof(the_pin) - 1);
1236 if (strchr(inflags, 'e'))
1238 if (strchr(inflags, 'E')) {
1249 struct ast_config *cfg;
1250 struct ast_variable *var;
1253 memset(map, 0, sizeof(map));
1255 ast_mutex_lock(&conflock);
1258 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
1259 /* Disqualify in use conference */
1260 if (confno_int >= 0 && confno_int < 1024)
1265 ast_mutex_unlock(&conflock);
1267 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
1268 if ((empty_no_pin) || (!dynamic)) {
1269 cfg = ast_load("meetme.conf");
1271 var = ast_variable_browse(cfg, "rooms");
1273 if (!strcasecmp(var->name, "conf")) {
1274 char *stringp = ast_strdupa(var->value);
1276 char *confno_tmp = strsep(&stringp, "|,");
1278 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
1279 if ((confno_int >= 0) && (confno_int < 1024)) {
1280 if (stringp && empty_no_pin) {
1286 /* For static: run through the list and see if this conference is empty */
1287 ast_mutex_lock(&conflock);
1290 if (!strcmp(confno_tmp, cnf->confno)) {
1291 /* The conference exists, therefore it's not empty */
1297 ast_mutex_unlock(&conflock);
1299 /* At this point, we have a confno_tmp (static conference) that is empty */
1300 if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) {
1301 /* Case 1: empty_no_pin and pin is nonexistant (NULL)
1302 * Case 2: empty_no_pin and pin is blank (but not NULL)
1303 * Case 3: not empty_no_pin
1305 strncpy(confno, confno_tmp, sizeof(confno) - 1);
1307 /* XXX the map is not complete (but we do have a confno) */
1312 ast_log(LOG_ERROR, "Out of memory\n");
1320 /* Select first conference number not in use */
1321 if (ast_strlen_zero(confno) && dynamic) {
1322 for (i=0;i<1024;i++) {
1324 snprintf(confno, sizeof(confno), "%d", i);
1331 if (ast_strlen_zero(confno)) {
1332 res = ast_streamfile(chan, "conf-noempty", chan->language);
1334 ast_waitstream(chan, "");
1336 if (sscanf(confno, "%d", &confno_int) == 1) {
1337 res = ast_streamfile(chan, "conf-enteringno", chan->language);
1339 ast_waitstream(chan, "");
1340 res = ast_say_digits(chan, confno_int, "", chan->language);
1343 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
1347 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
1348 /* Prompt user for conference number */
1349 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
1351 /* Don't try to validate when we catch an error */
1357 if (!ast_strlen_zero(confno)) {
1358 /* Check the validity of the conference */
1359 cnf = find_conf(chan, confno, 1, dynamic, the_pin);
1361 res = ast_streamfile(chan, "conf-invalid", chan->language);
1363 ast_waitstream(chan, "");
1368 if (!ast_strlen_zero(cnf->pin)) {
1369 char pin[AST_MAX_EXTENSION]="";
1372 /* Allow the pin to be retried up to 3 times */
1373 for (j=0; j<3; j++) {
1375 strncpy(pin, the_pin, sizeof(pin) - 1);
1378 /* Prompt user for pin if pin is required */
1379 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
1382 if (!strcasecmp(pin, cnf->pin)) {
1385 /* Run the conference */
1386 res = conf_run(chan, cnf, confflags);
1390 res = ast_streamfile(chan, "conf-invalidpin", chan->language);
1392 ast_waitstream(chan, AST_DIGIT_ANY);
1407 /* Don't retry pin with a static pin */
1413 /* No pin required */
1416 /* Run the conference */
1417 res = conf_run(chan, cnf, confflags);
1421 } while (allowretry);
1422 /* Do the conference */
1423 LOCAL_USER_REMOVE(u);
1427 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
1428 struct ast_conf_user *user = NULL;
1429 char usrno[1024] = "";
1430 if (conf && callerident) {
1431 user = conf->firstuser;
1433 snprintf(usrno, sizeof(usrno), "%i", user->user_no);
1434 if (strcmp(usrno, callerident) == 0)
1436 user = user->nextuser;
1442 /*--- admin_exec: The MeetMeadmin application */
1443 /* MeetMeAdmin(confno, command, caller) */
1444 static int admin_exec(struct ast_channel *chan, void *data) {
1445 char *params, *command = NULL, *caller = NULL, *conf = NULL;
1446 struct ast_conference *cnf;
1447 struct ast_conf_user *user = NULL;
1449 ast_mutex_lock(&conflock);
1450 /* The param has the conference number the user and the command to execute */
1451 if (data && !ast_strlen_zero(data)) {
1452 params = ast_strdupa((char *) data);
1453 conf = strsep(¶ms, "|");
1454 command = strsep(¶ms, "|");
1455 caller = strsep(¶ms, "|");
1457 ast_mutex_lock(&conflock);
1460 if (strcmp(cnf->confno, conf) == 0)
1464 ast_mutex_unlock(&conflock);
1467 user = find_user(cnf, caller);
1470 switch((int) (*command)) {
1471 case 76: /* L: Lock */
1474 case 108: /* l: Unlock */
1477 case 75: /* K: kick all users*/
1478 user = cnf->firstuser;
1480 user->adminflags |= ADMINFLAG_KICKME;
1481 if (user->nextuser) {
1482 user = user->nextuser;
1488 case 77: /* M: Mute */
1490 user->adminflags |= ADMINFLAG_MUTED;
1492 ast_log(LOG_NOTICE, "Specified User not found!");
1495 case 109: /* m: Unmute */
1496 if (user && (user->adminflags & ADMINFLAG_MUTED)) {
1497 user->adminflags ^= ADMINFLAG_MUTED;
1499 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
1502 case 107: /* k: Kick user */
1504 user->adminflags |= ADMINFLAG_KICKME;
1506 ast_log(LOG_NOTICE, "Specified User not found!");
1511 ast_log(LOG_NOTICE, "Conference Number not found\n");
1514 ast_mutex_unlock(&conflock);
1518 int unload_module(void)
1520 STANDARD_HANGUP_LOCALUSERS;
1521 ast_cli_unregister(&cli_show_confs);
1522 ast_cli_unregister(&cli_conf);
1523 ast_unregister_application(app3);
1524 ast_unregister_application(app2);
1525 return ast_unregister_application(app);
1528 int load_module(void)
1530 ast_cli_register(&cli_show_confs);
1531 ast_cli_register(&cli_conf);
1532 ast_register_application(app3, admin_exec, synopsis3, descrip3);
1533 ast_register_application(app2, count_exec, synopsis2, descrip2);
1534 return ast_register_application(app, conf_exec, synopsis, descrip);
1537 char *description(void)
1545 STANDARD_USECOUNT(res);
1551 return ASTERISK_GPL_KEY;