2 * Asterisk -- A telephony toolkit for Linux.
4 * Meet me conference bridge
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
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 " 'd' -- dynamically add conference\n"
63 " 'D' -- dynamically add conference, prompting for a PIN\n"
64 " 'e' -- select an empty conference\n"
65 " 'E' -- select an empty pinless conference\n"
66 " 'v' -- video mode\n"
67 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
68 " 'M' -- enable music on hold when the conference has a single caller\n"
69 " 'x' -- exit the conference if the last marked user left\n"
70 " 'w' -- wait until a marked user has entered the conference\n"
71 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
72 " Default: conf-background.agi\n"
73 " (Note: This does not work with non-Zap channels in the same conference)\n"
74 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
75 " 'a' -- set admin mode\n";
77 static char *descrip2 =
78 " MeetMeCount(confno[|var]): Plays back the number of users in the specifiedi\n"
79 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
80 "will be returned in the variable. Returns 0 on success or -1 on a hangup.\n"
81 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
83 static char *descrip3 =
84 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
85 " 'K' -- Kick all users out of conference\n"
86 " 'k' -- Kick one user out of conference\n"
87 " 'L' -- Lock conference\n"
88 " 'l' -- Unlock conference\n"
89 " 'M' -- Mute conference\n"
90 " 'm' -- Unmute conference\n"
97 static struct ast_conference {
98 char confno[AST_MAX_EXTENSION]; /* Conference */
99 struct ast_channel *chan; /* Announcements channel */
100 int fd; /* Announcements fd */
101 int zapconf; /* Zaptel Conf # */
102 int users; /* Number of active users */
103 int markedusers; /* Number of marked users */
104 struct ast_conf_user *firstuser; /* Pointer to the first user struct */
105 struct ast_conf_user *lastuser; /* Pointer to the last user struct */
106 time_t start; /* Start time (s) */
107 int isdynamic; /* Created on the fly? */
108 int locked; /* Is the conference locked? */
109 char pin[AST_MAX_EXTENSION]; /* If protected by a PIN */
110 struct ast_conference *next;
113 struct ast_conf_user {
114 int user_no; /* User Number */
115 struct ast_conf_user *prevuser; /* Pointer to the previous user */
116 struct ast_conf_user *nextuser; /* Pointer to the next user */
117 int userflags; /* Flags as set in the conference */
118 int adminflags; /* Flags set by the Admin */
119 struct ast_channel *chan; /* Connected channel */
120 char usrvalue[50]; /* Custom User Value */
121 time_t jointime; /* Time the user joined the conference */
124 #define ADMINFLAG_MUTED (1 << 1) /* User is muted */
125 #define ADMINFLAG_KICKME (1 << 2) /* User is kicked */
128 AST_MUTEX_DEFINE_STATIC(conflock);
130 static int admin_exec(struct ast_channel *chan, void *data);
138 #define CONF_SIZE 320
140 #define CONFFLAG_ADMIN (1 << 1) /* If set the user has admin access on the conference */
141 #define CONFFLAG_MONITOR (1 << 2) /* If set the user can only receive audio from the conference */
142 #define CONFFLAG_POUNDEXIT (1 << 3) /* If set asterisk will exit conference when '#' is pressed */
143 #define CONFFLAG_STARMENU (1 << 4) /* If set asterisk will provide a menu to the user what '*' is pressed */
144 #define CONFFLAG_TALKER (1 << 5) /* If set the use can only send audio to the conference */
145 #define CONFFLAG_QUIET (1 << 6) /* If set there will be no enter or leave sounds */
146 #define CONFFLAG_VIDEO (1 << 7) /* Set to enable video mode */
147 #define CONFFLAG_AGI (1 << 8) /* Set to run AGI Script in Background */
148 #define CONFFLAG_MOH (1 << 9) /* Set to have music on hold when user is alone in conference */
149 #define CONFFLAG_ADMINEXIT (1 << 10) /* If set the MeetMe will return if all marked with this flag left */
150 #define CONFFLAG_WAITMARKED (1 << 11) /* If set, the MeetMe will wait until a marked user enters */
153 static int careful_write(int fd, unsigned char *data, int len)
158 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
159 res = ioctl(fd, ZT_IOMUX, &x);
161 res = write(fd, data, len);
163 if (errno != EAGAIN) {
164 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
175 static void conf_play(struct ast_conference *conf, int sound)
179 ast_mutex_lock(&conflock);
194 careful_write(conf->fd, data, len);
195 ast_mutex_unlock(&conflock);
198 static struct ast_conference *build_conf(char *confno, char *pin, int make, int dynamic)
200 struct ast_conference *cnf;
201 struct zt_confinfo ztc;
202 ast_mutex_lock(&conflock);
205 if (!strcmp(confno, cnf->confno))
209 if (!cnf && (make || dynamic)) {
210 cnf = malloc(sizeof(struct ast_conference));
213 memset(cnf, 0, sizeof(struct ast_conference));
214 strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1);
215 strncpy(cnf->pin, pin, sizeof(cnf->pin) - 1);
216 cnf->markedusers = -1;
217 cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo");
219 cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */
221 ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
222 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
224 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
230 memset(&ztc, 0, sizeof(ztc));
231 /* Setup a new zap conference */
234 ztc.confmode = ZT_CONF_CONFANN;
235 if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
236 ast_log(LOG_WARNING, "Error setting conference\n");
238 ast_hangup(cnf->chan);
245 /* Fill the conference struct */
246 cnf->start = time(NULL);
247 cnf->zapconf = ztc.confno;
248 cnf->isdynamic = dynamic;
249 cnf->firstuser = NULL;
250 cnf->lastuser = NULL;
252 if (option_verbose > 2)
253 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
257 ast_log(LOG_WARNING, "Out of memory\n");
260 ast_mutex_unlock(&conflock);
264 static int confs_show(int fd, int argc, char **argv)
266 ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
267 return RESULT_SUCCESS;
270 static char show_confs_usage[] =
271 "Deprecated! Please use 'meetme' instead.\n";
273 static struct ast_cli_entry cli_show_confs = {
274 { "show", "conferences", NULL }, confs_show,
275 "Show status of conferences", show_confs_usage, NULL };
277 static int conf_cmd(int fd, int argc, char **argv) {
278 /* Process the command */
279 struct ast_conference *cnf;
280 struct ast_conf_user *user;
282 int i = 0, total = 0;
284 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
285 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
286 char cmdline[1024] = "";
289 ast_cli(fd, "Invalid Arguments.\n");
290 /* Check for length so no buffer will overflow... */
291 for (i = 0; i < argc; i++) {
292 if (strlen(argv[i]) > 100)
293 ast_cli(fd, "Invalid Arguments.\n");
296 /* 'MeetMe': List all the conferences */
300 ast_cli(fd, "No active MeetMe conferences.\n");
301 return RESULT_SUCCESS;
303 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
305 if (cnf->markedusers < 0)
306 strncpy(cmdline, "N/A ", sizeof(cmdline) - 1);
308 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
309 hr = (now - cnf->start) / 3600;
310 min = ((now - cnf->start) % 3600) / 60;
311 sec = (now - cnf->start) % 60;
313 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
318 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
319 return RESULT_SUCCESS;
322 return RESULT_SHOWUSAGE;
323 strncpy(cmdline, argv[2], sizeof(cmdline) - 1); /* Argv 2: conference number */
324 if (strstr(argv[1], "lock")) {
325 if (strcmp(argv[1], "lock") == 0) {
327 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
330 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
332 } else if (strstr(argv[1], "mute")) {
334 return RESULT_SHOWUSAGE;
335 if (strcmp(argv[1], "mute") == 0) {
337 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
338 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
341 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
342 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
344 } else if (strcmp(argv[1], "kick") == 0) {
346 return RESULT_SHOWUSAGE;
347 if (strcmp(argv[3], "all") == 0) {
349 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
351 /* Kick a single user */
352 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
353 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
355 } else if(strcmp(argv[1], "list") == 0) {
356 /* List all the users in a conference */
358 ast_cli(fd, "No active conferences.\n");
359 return RESULT_SUCCESS;
362 /* Find the right conference */
364 if (strcmp(cnf->confno, argv[2]) == 0)
369 ast_cli(fd, "No such conference: %s.\n",argv[2]);
370 return RESULT_SUCCESS;
373 /* Show all the users */
374 user = cnf->firstuser;
376 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)" : "" );
377 user = user->nextuser;
379 return RESULT_SUCCESS;
381 return RESULT_SHOWUSAGE;
382 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
383 admin_exec(NULL, cmdline);
387 static char *complete_confcmd(char *line, char *word, int pos, int state) {
388 #define CONF_COMMANDS 6
389 int which = 0, x = 0;
390 struct ast_conference *cnf = NULL;
391 struct ast_conf_user *usr = NULL;
394 char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"};
399 for (x = 0;x < CONF_COMMANDS; x++) {
400 if (!strncasecmp(cmds[x], word, strlen(word))) {
401 if (++which > state) {
402 return strdup(cmds[x]);
406 } else if (pos == 2) {
407 /* Conference Number */
408 ast_mutex_lock(&conflock);
411 if (!strncasecmp(word, cnf->confno, strlen(word))) {
417 ast_mutex_unlock(&conflock);
418 return cnf ? strdup(cnf->confno) : NULL;
419 } else if (pos == 3) {
420 /* User Number || Conf Command option*/
421 if (strstr(line, "mute") || strstr(line, "kick")) {
422 if ((state == 0) && (strstr(line, "kick")) && !(strncasecmp(word, "all", strlen(word)))) {
423 return strdup("all");
426 ast_mutex_lock(&conflock);
429 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
430 myline = ast_strdupa(line);
431 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
432 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
437 if (strcmp(confno, cnf->confno) == 0) {
443 /* Search for the user */
444 usr = cnf->firstuser;
446 snprintf(usrno, sizeof(usrno), "%i", usr->user_no);
447 if (!strncasecmp(word, usrno, strlen(word))) {
454 ast_mutex_unlock(&conflock);
455 return usr ? strdup(usrno) : NULL;
461 static char conf_usage[] =
462 "Usage: meetme (un)lock|(un)mute|kick|list <confno> <usernumber>\n"
463 " Executes a command for the conference or on a conferee\n";
465 static struct ast_cli_entry cli_conf = {
466 { "meetme", NULL, NULL }, conf_cmd,
467 "Execute a command on a conference or conferee", conf_usage, complete_confcmd };
469 static int confnonzero(void *ptr)
471 struct ast_conference *conf = ptr;
473 ast_mutex_lock(&conflock);
474 res = (conf->markedusers < 0);
475 ast_mutex_unlock(&conflock);
479 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
481 struct ast_conference *prev=NULL, *cur;
482 struct ast_conf_user *user = malloc(sizeof(struct ast_conf_user));
484 struct zt_confinfo ztc;
486 struct ast_channel *c;
501 int using_pseudo = 0;
505 char *agifiledefault = "conf-background.agi";
506 char meetmesecs[30] = "";
509 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
510 char *buf = __buf + AST_FRIENDLY_OFFSET;
513 ast_log(LOG_ERROR, "Out of memory\n");
516 memset(user, 0, sizeof(struct ast_conf_user));
518 user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */
521 /* Sorry, but this confernce is locked! */
522 if (!ast_streamfile(chan, "conf-locked", chan->language))
523 ast_waitstream(chan, "");
527 if (confflags & CONFFLAG_ADMINEXIT) {
528 if (conf->markedusers == -1) {
529 conf->markedusers = 1;
535 ast_mutex_lock(&conflock);
536 if (conf->firstuser == NULL) {
537 /* Fill the first new User struct */
539 user->nextuser = NULL;
540 user->prevuser = NULL;
541 conf->firstuser = user;
542 conf->lastuser = user;
544 /* Fill the new user struct */
545 user->user_no = conf->lastuser->user_no + 1;
546 user->prevuser = conf->lastuser;
547 user->nextuser = NULL;
548 if (conf->lastuser->nextuser != NULL) {
549 ast_log(LOG_WARNING, "Error in User Management!\n");
550 ast_mutex_unlock(&conflock);
553 conf->lastuser->nextuser = user;
554 conf->lastuser = user;
557 strncpy(user->usrvalue, "test", sizeof(user->usrvalue) - 1);
559 user->userflags = confflags;
560 user->adminflags = 0;
561 ast_mutex_unlock(&conflock);
562 origquiet = confflags & CONFFLAG_QUIET;
563 while((confflags & CONFFLAG_WAITMARKED) && (conf->markedusers < 0)) {
564 confflags &= ~CONFFLAG_QUIET;
565 confflags |= origquiet;
566 /* XXX Announce that we're waiting on the conference lead to join */
567 if (!(confflags & CONFFLAG_QUIET)) {
568 res = ast_streamfile(chan, "vm-dialout", chan->language);
570 res = ast_waitstream(chan, "");
573 /* If we're waiting with hold music, set to silent mode */
575 confflags |= CONFFLAG_QUIET;
576 ast_moh_start(chan, NULL);
577 res = ast_safe_sleep_conditional(chan, 60000, confnonzero, conf);
581 ast_log(LOG_DEBUG, "Got hangup on '%s' already\n", chan->name);
586 if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
587 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
588 if (ast_waitstream(chan, "") < 0)
594 /* Set it into linear mode (write) */
595 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
596 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
600 /* Set it into linear mode (read) */
601 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
602 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
605 ast_indicate(chan, -1);
606 retryzap = strcasecmp(chan->type, "Zap");
608 origfd = chan->fds[0];
610 fd = open("/dev/zap/pseudo", O_RDWR);
612 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
616 /* Make non-blocking */
617 flags = fcntl(fd, F_GETFL);
619 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
623 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
624 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
628 /* Setup buffering information */
629 memset(&bi, 0, sizeof(bi));
630 bi.bufsize = CONF_SIZE/2;
631 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
632 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
634 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
635 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
640 if (ioctl(fd, ZT_SETLINEAR, &x)) {
641 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
647 /* XXX Make sure we're not running on a pseudo channel XXX */
651 memset(&ztc, 0, sizeof(ztc));
652 /* Check to see if we're in a conference... */
654 if (ioctl(fd, ZT_GETCONF, &ztc)) {
655 ast_log(LOG_WARNING, "Error getting conference\n");
660 /* Whoa, already in a conference... Retry... */
662 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
667 memset(&ztc, 0, sizeof(ztc));
668 /* Add us to the conference */
670 ztc.confno = conf->zapconf;
671 if (confflags & CONFFLAG_MONITOR)
672 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
673 else if (confflags & CONFFLAG_TALKER)
674 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
676 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
678 if (ioctl(fd, ZT_SETCONF, &ztc)) {
679 ast_log(LOG_WARNING, "Error setting conference\n");
683 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
685 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
689 chan->name, chan->uniqueid, conf->confno);
691 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
693 if (!(confflags & CONFFLAG_QUIET))
694 conf_play(conf, ENTER);
697 if (confflags & CONFFLAG_AGI) {
699 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
700 or use default filename of conf-background.agi */
702 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
704 agifile = agifiledefault;
706 if (!strcasecmp(chan->type,"Zap")) {
707 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
709 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
711 /* Find a pointer to the agi app and execute the script */
712 app = pbx_findapp("agi");
714 ret = pbx_exec(chan, app, agifile, 1);
716 ast_log(LOG_WARNING, "Could not find application (agi)\n");
719 if (!strcasecmp(chan->type,"Zap")) {
720 /* Remove CONFMUTE mode on Zap channel */
722 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
725 if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
726 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
728 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
733 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
735 /* Update the struct with the actual confflags */
736 user->userflags = confflags;
738 /* trying to add moh for single person conf */
739 if (confflags & CONFFLAG_MOH) {
740 if (conf->users == 1) {
741 if (musiconhold == 0) {
742 ast_moh_start(chan, NULL);
753 /* Leave if the last marked user left */
754 if (conf->markedusers == 0) {
759 /* Check if the admin changed my modes */
760 if (user->adminflags) {
761 /* Set the new modes */
762 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
763 ztc.confmode ^= ZT_CONF_TALKER;
764 if (ioctl(fd, ZT_SETCONF, &ztc)) {
765 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
770 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
771 ztc.confmode |= ZT_CONF_TALKER;
772 if (ioctl(fd, ZT_SETCONF, &ztc)) {
773 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
778 if (user->adminflags & ADMINFLAG_KICKME) {
779 //You have been kicked.
780 if (!ast_streamfile(chan, "conf-kicked", chan->language))
781 ast_waitstream(chan, "");
785 } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
786 ztc.confmode |= ZT_CONF_TALKER;
787 if (ioctl(fd, ZT_SETCONF, &ztc)) {
788 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
795 if (c->fds[0] != origfd) {
797 /* Kill old pseudo */
800 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
808 if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
811 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
815 if ((confflags & CONFFLAG_ADMIN)) {
819 /* Record this sound! */
820 if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
821 ast_waitstream(chan, "");
823 switch(f->subclass - 48) {
824 case 1: /* Un/Mute */
826 if (ztc.confmode & ZT_CONF_TALKER) {
827 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
828 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
830 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
831 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
833 if (ioctl(fd, ZT_SETCONF, &ztc)) {
834 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
838 if (ztc.confmode & ZT_CONF_TALKER) {
839 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
840 ast_waitstream(chan, "");
842 if (!ast_streamfile(chan, "conf-muted", chan->language))
843 ast_waitstream(chan, "");
846 case 2: /* Un/Lock the Conference */
850 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
851 ast_waitstream(chan, "");
854 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
855 ast_waitstream(chan, "");
860 /* Play an error message! */
861 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
862 ast_waitstream(chan, "");
870 /* Record this sound! */
871 if (!ast_streamfile(chan, "conf-usermenu", chan->language))
872 ast_waitstream(chan, "");
874 switch(f->subclass - 48) {
875 case 1: /* Un/Mute */
877 if (ztc.confmode & ZT_CONF_TALKER) {
878 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
879 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
880 } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
881 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
882 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
884 if (ioctl(fd, ZT_SETCONF, &ztc)) {
885 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
889 if (ztc.confmode & ZT_CONF_TALKER) {
890 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
891 ast_waitstream(chan, "");
893 if (!ast_streamfile(chan, "conf-muted", chan->language))
894 ast_waitstream(chan, "");
899 /* Play an error message! */
900 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
901 ast_waitstream(chan, "");
907 ast_moh_start(chan, NULL);
909 } else if (using_pseudo) {
910 if (f->frametype == AST_FRAME_VOICE) {
911 if (f->subclass == AST_FORMAT_SLINEAR) {
912 /* Carefully write */
913 careful_write(fd, f->data, f->datalen);
915 ast_log(LOG_WARNING, "Huh? Got a non-linear (%d) frame in the conference\n", f->subclass);
919 } else if (outfd > -1) {
920 res = read(outfd, buf, CONF_SIZE);
922 memset(&fr, 0, sizeof(fr));
923 fr.frametype = AST_FRAME_VOICE;
924 fr.subclass = AST_FORMAT_SLINEAR;
928 fr.offset = AST_FRIENDLY_OFFSET;
929 if (ast_write(chan, &fr) < 0) {
930 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
934 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
941 /* Take out of conference */
942 /* Add us to the conference */
946 if (ioctl(fd, ZT_SETCONF, &ztc)) {
947 ast_log(LOG_WARNING, "Error setting conference\n");
950 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
951 conf_play(conf, LEAVE);
954 ast_mutex_lock(&conflock);
955 if (user->user_no) { /* Only cleanup users who really joined! */
956 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
960 chan->name, chan->uniqueid, conf->confno);
963 if (confflags & CONFFLAG_ADMINEXIT)
967 /* No more users -- close this one out */
971 prev->next = conf->next;
980 ast_log(LOG_WARNING, "Conference not found\n");
982 ast_hangup(conf->chan);
987 /* Remove the user struct */
988 if (user == conf->firstuser) {
989 if (user->nextuser) {
990 /* There is another entry */
991 user->nextuser->prevuser = NULL;
993 /* We are the only entry */
994 conf->lastuser = NULL;
997 conf->firstuser = user->nextuser;
998 } else if (user == conf->lastuser){
1000 user->prevuser->nextuser = NULL;
1002 ast_log(LOG_ERROR, "Bad bad bad! We're the last, not the first, but nobody before us??\n");
1003 conf->lastuser = user->prevuser;
1006 user->nextuser->prevuser = user->prevuser;
1008 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
1010 user->prevuser->nextuser = user->nextuser;
1012 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
1014 /* Return the number of seconds the user was in the conf */
1015 snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (user->jointime - time(NULL)));
1016 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1020 ast_mutex_unlock(&conflock);
1024 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
1026 struct ast_config *cfg;
1027 struct ast_variable *var;
1028 struct ast_conference *cnf;
1030 /* Check first in the conference list */
1031 ast_mutex_lock(&conflock);
1034 if (!strcmp(confno, cnf->confno))
1038 ast_mutex_unlock(&conflock);
1042 /* No need to parse meetme.conf */
1043 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1045 if (dynamic_pin[0] == 'q') {
1046 /* Query the user to enter a PIN */
1047 ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
1049 cnf = build_conf(confno, dynamic_pin, make, dynamic);
1051 cnf = build_conf(confno, "", make, dynamic);
1054 /* Check the config */
1055 cfg = ast_load("meetme.conf");
1057 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
1060 var = ast_variable_browse(cfg, "rooms");
1062 if (!strcasecmp(var->name, "conf")) {
1063 /* Separate the PIN */
1066 if ((pin = ast_strdupa(var->value))) {
1067 conf = strsep(&pin, "|,");
1068 if (!strcasecmp(conf, confno)) {
1069 /* Bingo it's a valid conference */
1071 cnf = build_conf(confno, pin, make, dynamic);
1073 cnf = build_conf(confno, "", make, dynamic);
1081 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1089 /*--- count_exec: The MeetmeCount application */
1090 static int count_exec(struct ast_channel *chan, void *data)
1092 struct localuser *u;
1094 struct ast_conference *conf;
1096 char *confnum, *localdata;
1099 if (!data || ast_strlen_zero(data)) {
1100 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1103 localdata = ast_strdupa(data);
1105 confnum = strsep(&localdata,"|");
1106 conf = find_conf(chan, confnum, 0, 0, NULL);
1108 count = conf->users;
1112 if (localdata && !ast_strlen_zero(localdata)){
1113 /* have var so load it and exit */
1114 snprintf(val,sizeof(val), "%i",count);
1115 pbx_builtin_setvar_helper(chan, localdata,val);
1117 if (chan->_state != AST_STATE_UP)
1119 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1121 LOCAL_USER_REMOVE(u);
1125 /*--- conf_exec: The meetme() application */
1126 static int conf_exec(struct ast_channel *chan, void *data)
1129 struct localuser *u;
1130 char confno[AST_MAX_EXTENSION] = "";
1133 struct ast_conference *cnf;
1136 int empty = 0, empty_no_pin = 0;
1137 char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
1139 if (!data || ast_strlen_zero(data)) {
1146 if (chan->_state != AST_STATE_UP)
1149 info = ast_strdupa((char *)notdata);
1152 char *tmp = strsep(&info, "|");
1153 strncpy(confno, tmp, sizeof(confno) - 1);
1154 if (ast_strlen_zero(confno)) {
1159 inflags = strsep(&info, "|");
1161 inpin = strsep(&info, "|");
1163 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
1166 if (strchr(inflags, 'a'))
1167 confflags |= CONFFLAG_ADMIN;
1168 if (strchr(inflags, 'm'))
1169 confflags |= CONFFLAG_MONITOR;
1170 if (strchr(inflags, 'p'))
1171 confflags |= CONFFLAG_POUNDEXIT;
1172 if (strchr(inflags, 's'))
1173 confflags |= CONFFLAG_STARMENU;
1174 if (strchr(inflags, 't'))
1175 confflags |= CONFFLAG_TALKER;
1176 if (strchr(inflags, 'q'))
1177 confflags |= CONFFLAG_QUIET;
1178 if (strchr(inflags, 'M'))
1179 confflags |= CONFFLAG_MOH;
1180 if (strchr(inflags, 'x'))
1181 confflags |= CONFFLAG_ADMINEXIT;
1182 if (strchr(inflags, 'b'))
1183 confflags |= CONFFLAG_AGI;
1184 if (strchr(inflags, 'w'))
1185 confflags |= CONFFLAG_WAITMARKED;
1186 if (strchr(inflags, 'd'))
1188 if (strchr(inflags, 'D')) {
1191 strncpy(the_pin, "q", sizeof(the_pin) - 1);
1194 if (strchr(inflags, 'e'))
1196 if (strchr(inflags, 'E')) {
1207 struct ast_config *cfg;
1208 struct ast_variable *var;
1211 memset(map, 0, sizeof(map));
1213 ast_mutex_lock(&conflock);
1216 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
1217 /* Disqualify in use conference */
1218 if (confno_int >= 0 && confno_int < 1024)
1223 ast_mutex_unlock(&conflock);
1225 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
1226 if ((empty_no_pin) || (!dynamic)) {
1227 cfg = ast_load("meetme.conf");
1229 var = ast_variable_browse(cfg, "rooms");
1231 if (!strcasecmp(var->name, "conf")) {
1232 char *stringp = ast_strdupa(var->value);
1234 char *confno_tmp = strsep(&stringp, "|,");
1236 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
1237 if ((confno_int >= 0) && (confno_int < 1024)) {
1238 if (stringp && empty_no_pin) {
1244 /* For static: run through the list and see if this conference is empty */
1245 ast_mutex_lock(&conflock);
1248 if (!strcmp(confno_tmp, cnf->confno)) {
1249 /* The conference exists, therefore it's not empty */
1255 ast_mutex_unlock(&conflock);
1257 /* At this point, we have a confno_tmp (static conference) that is empty */
1258 if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) {
1259 /* Case 1: empty_no_pin and pin is nonexistant (NULL)
1260 * Case 2: empty_no_pin and pin is blank (but not NULL)
1261 * Case 3: not empty_no_pin
1263 strncpy(confno, confno_tmp, sizeof(confno) - 1);
1265 /* XXX the map is not complete (but we do have a confno) */
1270 ast_log(LOG_ERROR, "Out of memory\n");
1278 /* Select first conference number not in use */
1279 if (ast_strlen_zero(confno) && dynamic) {
1280 for (i=0;i<1024;i++) {
1282 snprintf(confno, sizeof(confno), "%d", i);
1289 if (ast_strlen_zero(confno)) {
1290 res = ast_streamfile(chan, "conf-noempty", chan->language);
1292 ast_waitstream(chan, "");
1294 if (sscanf(confno, "%d", &confno_int) == 1) {
1295 res = ast_streamfile(chan, "conf-enteringno", chan->language);
1297 ast_waitstream(chan, "");
1298 res = ast_say_digits(chan, confno_int, "", chan->language);
1301 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
1305 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
1306 /* Prompt user for conference number */
1307 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
1309 /* Don't try to validate when we catch an error */
1315 if (!ast_strlen_zero(confno)) {
1316 /* Check the validity of the conference */
1317 cnf = find_conf(chan, confno, 1, dynamic, the_pin);
1319 res = ast_streamfile(chan, "conf-invalid", chan->language);
1321 ast_waitstream(chan, "");
1326 if (!ast_strlen_zero(cnf->pin)) {
1327 char pin[AST_MAX_EXTENSION];
1330 strncpy(pin, the_pin, sizeof(pin) - 1);
1333 /* Prompt user for pin if pin is required */
1334 res = ast_app_getdata(chan, "conf-getpin", pin, sizeof(pin) - 1, 0);
1337 if (!strcasecmp(pin, cnf->pin)) {
1340 /* Run the conference */
1341 res = conf_run(chan, cnf, confflags);
1344 res = ast_streamfile(chan, "conf-invalidpin", chan->language);
1346 ast_waitstream(chan, "");
1356 /* No pin required */
1359 /* Run the conference */
1360 res = conf_run(chan, cnf, confflags);
1364 } while (allowretry);
1365 /* Do the conference */
1366 LOCAL_USER_REMOVE(u);
1370 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
1371 struct ast_conf_user *user = NULL;
1372 char usrno[1024] = "";
1373 if (conf && callerident) {
1374 user = conf->firstuser;
1376 snprintf(usrno, sizeof(usrno), "%i", user->user_no);
1377 if (strcmp(usrno, callerident) == 0)
1379 user = user->nextuser;
1385 /*--- admin_exec: The MeetMeadmin application */
1386 /* MeetMeAdmin(confno, command, caller) */
1387 static int admin_exec(struct ast_channel *chan, void *data) {
1388 char *params, *command = NULL, *caller = NULL, *conf = NULL;
1389 struct ast_conference *cnf;
1390 struct ast_conf_user *user = NULL;
1392 ast_mutex_lock(&conflock);
1393 /* The param has the conference number the user and the command to execute */
1394 if (data && !ast_strlen_zero(data)) {
1395 params = ast_strdupa((char *) data);
1396 conf = strsep(¶ms, "|");
1397 command = strsep(¶ms, "|");
1398 caller = strsep(¶ms, "|");
1400 ast_mutex_lock(&conflock);
1403 if (strcmp(cnf->confno, conf) == 0)
1407 ast_mutex_unlock(&conflock);
1410 user = find_user(cnf, caller);
1413 switch((int) (*command)) {
1414 case 76: /* L: Lock */
1417 case 108: /* l: Unlock */
1420 case 75: /* K: kick all users*/
1421 user = cnf->firstuser;
1423 user->adminflags |= ADMINFLAG_KICKME;
1424 if (user->nextuser) {
1425 user = user->nextuser;
1431 case 77: /* M: Mute */
1433 user->adminflags |= ADMINFLAG_MUTED;
1435 ast_log(LOG_NOTICE, "Specified User not found!");
1438 case 109: /* m: Unmute */
1439 if (user && (user->adminflags & ADMINFLAG_MUTED)) {
1440 user->adminflags ^= ADMINFLAG_MUTED;
1442 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
1445 case 107: /* k: Kick user */
1447 user->adminflags |= ADMINFLAG_KICKME;
1449 ast_log(LOG_NOTICE, "Specified User not found!");
1454 ast_log(LOG_NOTICE, "Conference Number not found\n");
1457 ast_mutex_unlock(&conflock);
1461 int unload_module(void)
1463 STANDARD_HANGUP_LOCALUSERS;
1464 ast_cli_unregister(&cli_show_confs);
1465 ast_cli_unregister(&cli_conf);
1466 ast_unregister_application(app3);
1467 ast_unregister_application(app2);
1468 return ast_unregister_application(app);
1471 int load_module(void)
1473 ast_cli_register(&cli_show_confs);
1474 ast_cli_register(&cli_conf);
1475 ast_register_application(app3, admin_exec, synopsis3, descrip3);
1476 ast_register_application(app2, count_exec, synopsis2, descrip2);
1477 return ast_register_application(app, conf_exec, synopsis, descrip);
1480 char *description(void)
1488 STANDARD_USECOUNT(res);
1494 return ASTERISK_GPL_KEY;