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>
32 #include <sys/ioctl.h>
35 #include <linux/zaptel.h>
37 static char *tdesc = "Simple MeetMe conference bridge";
39 static char *app = "MeetMe";
40 static char *app2 = "MeetMeCount";
41 static char *app3 = "MeetMeAdmin";
43 static char *synopsis = "Simple MeetMe conference bridge";
44 static char *synopsis2 = "MeetMe participant count";
45 static char *synopsis3 = "Send Admin Commands to a conference";
47 static char *descrip =
48 " MeetMe(confno[,[options][,pin]]): Enters the user into a specified MeetMe conference.\n"
49 "If the conference number is omitted, the user will be prompted to enter\n"
50 "one. This application always returns -1. A ZAPTEL INTERFACE MUST BE INSTALLED\n"
51 "FOR CONFERENCING TO WORK!\n\n"
53 "The option string may contain zero or more of the following characters:\n"
54 " 'm' -- set monitor only mode (Listen only, no talking\n"
55 " 't' -- set talk only mode. (Talk only, no listening)\n"
56 " 'p' -- allow user to exit the conference by pressing '#'\n"
57 " 'd' -- dynamically add conference\n"
58 " 'D' -- dynamically add conference, prompting for a PIN\n"
59 " 'e' -- select an empty conference\n"
60 " 'E' -- select an empty pinless conference\n"
61 " 'v' -- video mode\n"
62 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
63 " 'M' -- enable music on hold when the conference has a single caller\n"
64 " 'x' -- exit the conference if the last marked user left\n"
65 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
66 " Default: conf-background.agi\n"
67 " (Note: This does not work with non-Zap channels in the same conference)\n"
68 " 'u' -- send user to admin/user menu if '*' is received\n"
69 " 'a' -- set admin mode\n";
71 static char *descrip2 =
72 " MeetMeCount(confno[|var]): Plays back the number of users in the specifiedi\n"
73 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
74 "will be returned in the variable. Returns 0 on success or -1 on a hangup.\n"
75 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
77 static char *descrip3 = "";
83 static struct ast_conference {
84 char confno[AST_MAX_EXTENSION]; /* Conference */
85 int fd; /* Announcements fd */
86 int zapconf; /* Zaptel Conf # */
87 int users; /* Number of active users */
88 int markedusers; /* Number of marked users */
89 struct ast_conf_user *firstuser; /* Pointer to the first user struct */
90 struct ast_conf_user *lastuser; /* Pointer to the last user struct */
91 time_t start; /* Start time (s) */
92 int isdynamic; /* Created on the fly? */
93 int locked; /* Is the conference locked? */
94 char pin[AST_MAX_EXTENSION]; /* If protected by a PIN */
95 struct ast_conference *next;
98 struct ast_conf_user {
99 int user_no; /* User Number */
100 struct ast_conf_user *prevuser; /* Pointer to the previous user */
101 struct ast_conf_user *nextuser; /* Pointer to the next user */
102 int userflags; /* Flags as set in the conference */
103 int adminflags; /* Flags set by the Admin */
104 struct ast_channel *chan; /* Connected channel */
105 char usrvalue[50]; /* Custom User Value */
106 time_t jointime; /* Time the user joined the conference */
109 #define ADMINFLAG_MUTED (1 << 1) /* User is muted */
110 #define ADMINFLAG_KICKME (1 << 2) /* User is kicked */
113 static ast_mutex_t conflock = AST_MUTEX_INITIALIZER;
115 static int admin_exec(struct ast_channel *chan, void *data);
123 #define CONF_SIZE 160
125 #define CONFFLAG_ADMIN (1 << 1) /* If set the user has admin access on the conference */
126 #define CONFFLAG_MONITOR (1 << 2) /* If set the user can only receive audio from the conference */
127 #define CONFFLAG_POUNDEXIT (1 << 3) /* If set asterisk will exit conference when '#' is pressed */
128 #define CONFFLAG_STARMENU (1 << 4) /* If set asterisk will provide a menu to the user what '*' is pressed */
129 #define CONFFLAG_TALKER (1 << 5) /* If set the use can only send audio to the conference */
130 #define CONFFLAG_QUIET (1 << 6) /* If set there will be no enter or leave sounds */
131 #define CONFFLAG_VIDEO (1 << 7) /* Set to enable video mode */
132 #define CONFFLAG_AGI (1 << 8) /* Set to run AGI Script in Background */
133 #define CONFFLAG_MOH (1 << 9) /* Set to have music on hold when */
134 #define CONFFLAG_ADMINEXIT (1 << 10) /* If set the MeetMe will return if all marked with this flag left */
137 static int careful_write(int fd, unsigned char *data, int len)
141 res = write(fd, data, len);
143 if (errno != EAGAIN) {
144 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
155 static void conf_play(struct ast_conference *conf, int sound)
159 ast_mutex_lock(&conflock);
174 careful_write(conf->fd, data, len);
175 ast_mutex_unlock(&conflock);
178 static struct ast_conference *build_conf(char *confno, char *pin, int make, int dynamic)
180 struct ast_conference *cnf;
181 struct zt_confinfo ztc;
182 ast_mutex_lock(&conflock);
185 if (!strcmp(confno, cnf->confno))
189 if (!cnf && (make || dynamic)) {
190 cnf = malloc(sizeof(struct ast_conference));
193 memset(cnf, 0, sizeof(struct ast_conference));
194 strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1);
195 strncpy(cnf->pin, pin, sizeof(cnf->pin) - 1);
196 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
198 ast_log(LOG_WARNING, "Unable to open pseudo channel\n");
203 memset(&ztc, 0, sizeof(ztc));
204 /* Setup a new zap conference */
207 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
208 if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
209 ast_log(LOG_WARNING, "Error setting conference\n");
215 /* Fill the conference struct */
216 cnf->start = time(NULL);
217 cnf->zapconf = ztc.confno;
218 cnf->isdynamic = dynamic;
219 cnf->firstuser = NULL;
220 cnf->lastuser = NULL;
222 if (option_verbose > 2)
223 ast_verbose(VERBOSE_PREFIX_3 "Created ZapTel conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
227 ast_log(LOG_WARNING, "Out of memory\n");
230 ast_mutex_unlock(&conflock);
234 static int confs_show(int fd, int argc, char **argv)
236 ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
237 return RESULT_SUCCESS;
240 static char show_confs_usage[] =
241 "Deprecated! Please use 'meetme' instead.\n";
243 static struct ast_cli_entry cli_show_confs = {
244 { "show", "conferences", NULL }, confs_show,
245 "Show status of conferences", show_confs_usage, NULL };
247 static int conf_cmd(int fd, int argc, char **argv) {
248 /* Process the command */
249 struct ast_conference *cnf;
250 struct ast_conf_user *user;
254 char *header_format = "%-14s %-14s %-8s %-8s\n";
255 char *data_format = "%-12.12s %4.4d %02d:%02d:%02d %-8s\n";
256 char cmdline[1024] = "";
258 ast_cli(fd, "Invalid Arguments.\n");
259 /* Check for length so no buffer will overflow... */
260 for (i = 0; i < argc; i++) {
261 if (strlen(argv[i]) > 100)
262 ast_cli(fd, "Invalid Arguments.\n");
265 /* List all the conferences */
269 ast_cli(fd, "No active conferences.\n");
270 return RESULT_SUCCESS;
272 ast_cli(fd, header_format, "Conf Num", "Parties", "Activity", "Creation");
274 hr = (now - cnf->start) / 3600;
275 min = ((now - cnf->start) % 3600) / 60;
276 sec = (now - cnf->start) % 60;
279 ast_cli(fd, data_format, cnf->confno, cnf->users, hr, min, sec, "Dynamic");
281 ast_cli(fd, data_format, cnf->confno, cnf->users, hr, min, sec, "Static");
285 return RESULT_SUCCESS;
288 return RESULT_SHOWUSAGE;
289 strncpy(cmdline, argv[2], 100);
290 if (strstr(argv[1], "lock")) {
291 if (strcmp(argv[1], "lock") == 0) {
293 strcat(cmdline, "|L");
296 strcat(cmdline, "|l");
298 } else if (strstr(argv[1], "mute")) {
300 return RESULT_SHOWUSAGE;
301 if (strcmp(argv[1], "mute") == 0) {
303 strcat(cmdline, "|M|");
304 strcat(cmdline, argv[3]);
307 strcat(cmdline, "|m|");
308 strcat(cmdline, argv[3]);
310 } else if (strcmp(argv[1], "kick") == 0) {
312 return RESULT_SHOWUSAGE;
313 if (strcmp(argv[3], "all") == 0) {
315 strcat(cmdline, "|K");
317 /* Kick a single user */
318 strcat(cmdline, "|k|");
319 strcat(cmdline, argv[3]);
321 } else if(strcmp(argv[1], "list") == 0) {
322 /* List all the users in a conference */
324 ast_cli(fd, "No active conferences.\n");
325 return RESULT_SUCCESS;
328 /* Find the right conference */
330 if (strcmp(cnf->confno, argv[2]) == 0)
335 ast_cli(fd, "No such conference: %s.\n",argv[2]);
336 return RESULT_SUCCESS;
339 /* Show all the users */
340 user = cnf->firstuser;
342 ast_cli(fd, "User Number: %i on Channel: %s\n", user->user_no, user->chan->name);
343 user = user->nextuser;
345 return RESULT_SUCCESS;
347 return RESULT_SHOWUSAGE;
348 ast_log(LOG_NOTICE, "Cmdline: %s\n", cmdline);
349 admin_exec(NULL, cmdline);
353 static char *complete_confcmd(char *line, char *word, int pos, int state) {
354 #define CONF_COMMANDS 6
355 int which = 0, x = 0;
356 struct ast_conference *cnf = NULL;
357 struct ast_conf_user *usr = NULL;
360 char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"};
365 for (x = 0;x < CONF_COMMANDS; x++) {
366 if (!strncasecmp(cmds[x], word, strlen(word))) {
367 if (++which > state) {
368 return strdup(cmds[x]);
372 } else if (pos == 2) {
373 /* Conference Number */
374 ast_mutex_lock(&conflock);
377 if (!strncasecmp(word, cnf->confno, strlen(word))) {
383 ast_mutex_unlock(&conflock);
384 return cnf ? strdup(cnf->confno) : NULL;
385 } else if (pos == 3) {
386 /* User Number || Conf Command option*/
387 if (strstr(line, "mute") || strstr(line, "kick")) {
388 if ((state == 0) && (strstr(line, "kick")) && !(strncasecmp(word, "all", strlen(word)))) {
389 return strdup("all");
392 ast_mutex_lock(&conflock);
395 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
396 myline = strdupa(line);
397 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
398 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
403 if (strcmp(confno, cnf->confno) == 0) {
409 /* Search for the user */
410 usr = cnf->firstuser;
412 sprintf(usrno, "%i", usr->user_no);
413 if (!strncasecmp(word, usrno, strlen(word))) {
420 ast_mutex_unlock(&conflock);
421 return usr ? strdup(usrno) : NULL;
427 static char conf_usage[] =
428 "Usage: meetme (un)lock|(un)mute|kick|list confno usernumber\n"
429 " Executes a command for the conference or on a conferee\n";
431 static struct ast_cli_entry cli_conf = {
432 { "meetme", NULL, NULL }, conf_cmd,
433 "Execute a command on a conference or conferee", conf_usage, complete_confcmd };
435 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
437 struct ast_conference *prev=NULL, *cur;
438 struct ast_conf_user *user = malloc(sizeof(struct ast_conf_user));
440 struct zt_confinfo ztc;
442 struct ast_channel *c;
459 char *agifiledefault = "conf-background.agi";
463 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
464 char *buf = __buf + AST_FRIENDLY_OFFSET;
466 user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */
469 /* Sorry, but this confernce is locked! */
470 if (!ast_streamfile(chan, "conf-locked", chan->language))
471 ast_waitstream(chan, "");
476 if (confflags & CONFFLAG_ADMINEXIT) {
477 if (conf->markedusers == -1) {
478 conf->markedusers = 1;
484 ast_mutex_lock(&conflock);
485 if (conf->firstuser == NULL) {
486 /* Fill the first new User struct */
488 user->nextuser = NULL;
489 user->prevuser = NULL;
490 conf->firstuser = user;
491 conf->lastuser = user;
493 /* Fill the new user struct */
494 user->user_no = conf->lastuser->user_no + 1;
495 user->prevuser = conf->lastuser;
496 user->nextuser = NULL;
497 if (conf->lastuser->nextuser != NULL) {
498 ast_log(LOG_WARNING, "Error in User Management!\n");
501 conf->lastuser->nextuser = user;
502 conf->lastuser = user;
505 strncpy(user->usrvalue, "test", sizeof(user->usrvalue));
507 user->userflags = confflags;
508 user->adminflags = 0;
509 ast_mutex_unlock(&conflock);
511 if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
512 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
513 ast_waitstream(chan, "");
516 if (confflags & CONFFLAG_VIDEO) {
517 /* Set it into linear mode (write) */
518 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
519 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
523 /* Set it into linear mode (read) */
524 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
525 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
529 /* Set it into U-law mode (write) */
530 if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
531 ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
535 /* Set it into U-law mode (read) */
536 if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
537 ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
541 ast_indicate(chan, -1);
542 retryzap = strcasecmp(chan->type, "Zap");
544 origfd = chan->fds[0];
546 fd = open("/dev/zap/pseudo", O_RDWR);
548 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
551 /* Make non-blocking */
552 flags = fcntl(fd, F_GETFL);
554 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
558 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
559 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
563 /* Setup buffering information */
564 memset(&bi, 0, sizeof(bi));
565 bi.bufsize = CONF_SIZE;
566 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
567 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
569 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
570 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
574 if (confflags & CONFFLAG_VIDEO) {
576 if (ioctl(fd, ZT_SETLINEAR, &x)) {
577 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
584 /* XXX Make sure we're not running on a pseudo channel XXX */
588 memset(&ztc, 0, sizeof(ztc));
589 /* Check to see if we're in a conference... */
591 if (ioctl(fd, ZT_GETCONF, &ztc)) {
592 ast_log(LOG_WARNING, "Error getting conference\n");
597 /* Whoa, already in a conference... Retry... */
599 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
604 memset(&ztc, 0, sizeof(ztc));
605 /* Add us to the conference */
607 ztc.confno = conf->zapconf;
608 if (confflags & CONFFLAG_MONITOR)
609 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
610 else if (confflags & CONFFLAG_TALKER)
611 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
613 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
615 if (ioctl(fd, ZT_SETCONF, &ztc)) {
616 ast_log(LOG_WARNING, "Error setting conference\n");
620 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
622 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
626 chan->name, chan->uniqueid, conf->confno);
628 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
630 if (!(confflags & CONFFLAG_QUIET))
631 conf_play(conf, ENTER);
634 if (confflags & CONFFLAG_AGI) {
636 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
637 or use default filename of conf-background.agi */
639 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
641 agifile = agifiledefault;
643 if (!strcasecmp(chan->type,"Zap")) {
644 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
646 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
648 /* Find a pointer to the agi app and execute the script */
649 app = pbx_findapp("agi");
651 ret = pbx_exec(chan, app, agifile, 1);
653 ast_log(LOG_WARNING, "Could not find application (agi)\n");
656 if (!strcasecmp(chan->type,"Zap")) {
657 /* Remove CONFMUTE mode on Zap channel */
659 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
662 if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
663 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
665 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
670 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
672 /* Update the struct with the actual confflags */
673 user->userflags = confflags;
675 /* trying to add moh for single person conf */
676 if (confflags & CONFFLAG_MOH) {
677 if (conf->users == 1) {
678 if (musiconhold == 0) {
679 ast_moh_start(chan, NULL);
690 /* Leave if the last marked user left */
691 if ((confflags & CONFFLAG_ADMINEXIT) && (conf->markedusers == 0)) {
696 /* Check if the admin changed my modes */
697 if (user->adminflags) {
698 /* Set the new modes */
699 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
700 ztc.confmode ^= ZT_CONF_TALKER;
701 if (ioctl(fd, ZT_SETCONF, &ztc)) {
702 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
707 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
708 ztc.confmode |= ZT_CONF_TALKER;
709 if (ioctl(fd, ZT_SETCONF, &ztc)) {
710 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
715 if (user->adminflags & ADMINFLAG_KICKME) {
716 //You have been kicked.
717 if (!ast_streamfile(chan, "conf-kicked", chan->language))
718 ast_waitstream(chan, "");
722 } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
723 ztc.confmode |= ZT_CONF_TALKER;
724 if (ioctl(fd, ZT_SETCONF, &ztc)) {
725 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
732 if (c->fds[0] != origfd) {
734 /* Kill old pseudo */
737 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
744 if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
747 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
751 if ((confflags & CONFFLAG_ADMIN)) {
755 /* Record this sound! */
756 if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
757 ast_waitstream(chan, "");
759 switch(f->subclass - 48) {
760 case 1: /* Un/Mute */
762 if (ztc.confmode & ZT_CONF_TALKER) {
763 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
764 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
766 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
767 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
769 if (ioctl(fd, ZT_SETCONF, &ztc)) {
770 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
774 if (ztc.confmode & ZT_CONF_TALKER) {
775 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
776 ast_waitstream(chan, "");
778 if (!ast_streamfile(chan, "conf-muted", chan->language))
779 ast_waitstream(chan, "");
782 case 2: /* Un/Lock the Conference */
786 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
787 ast_waitstream(chan, "");
790 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
791 ast_waitstream(chan, "");
797 /* Play an error message! */
798 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
799 ast_waitstream(chan, "");
807 /* Record this sound! */
808 if (!ast_streamfile(chan, "conf-usermenu", chan->language))
809 ast_waitstream(chan, "");
811 switch(f->subclass - 48) {
812 case 1: /* Un/Mute */
814 if (ztc.confmode & ZT_CONF_TALKER) {
815 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
816 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
817 } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
818 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
819 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
821 if (ioctl(fd, ZT_SETCONF, &ztc)) {
822 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
826 if (ztc.confmode & ZT_CONF_TALKER) {
827 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
828 ast_waitstream(chan, "");
830 if (!ast_streamfile(chan, "conf-muted", chan->language))
831 ast_waitstream(chan, "");
836 /* Play an error message! */
837 if (!ast_streamfile(chan, "errormenu", chan->language))
838 ast_waitstream(chan, "");
844 ast_moh_start(chan, NULL);
846 } else if (fd != chan->fds[0]) {
847 if (f->frametype == AST_FRAME_VOICE) {
848 if (f->subclass == AST_FORMAT_ULAW) {
849 /* Carefully write */
850 careful_write(fd, f->data, f->datalen);
852 ast_log(LOG_WARNING, "Huh? Got a non-ulaw (%d) frame in the conference\n", f->subclass);
856 } else if (outfd > -1) {
857 res = read(outfd, buf, CONF_SIZE);
859 memset(&fr, 0, sizeof(fr));
860 fr.frametype = AST_FRAME_VOICE;
861 fr.subclass = AST_FORMAT_ULAW;
865 fr.offset = AST_FRIENDLY_OFFSET;
866 if (ast_write(chan, &fr) < 0) {
867 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
871 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
875 if (fd != chan->fds[0])
878 /* Take out of conference */
879 /* Add us to the conference */
883 if (ioctl(fd, ZT_SETCONF, &ztc)) {
884 ast_log(LOG_WARNING, "Error setting conference\n");
887 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
888 conf_play(conf, LEAVE);
891 if (user->user_no) { /* Only cleanup users who really joined! */
892 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
896 chan->name, chan->uniqueid, conf->confno);
897 ast_mutex_lock(&conflock);
901 /* No more users -- close this one out */
905 prev->next = conf->next;
914 ast_log(LOG_WARNING, "Conference not found\n");
918 /* Remove the user struct */
919 if (user == cur->firstuser) {
920 cur->firstuser->nextuser->prevuser = NULL;
921 cur->firstuser = cur->firstuser->nextuser;
922 } else if (user == cur->lastuser){
923 cur->lastuser->prevuser->nextuser = NULL;
924 cur->lastuser = cur->lastuser->prevuser;
926 user->nextuser->prevuser = user->prevuser;
927 user->prevuser->nextuser = user->nextuser;
929 /* Return the number of seconds the user was in the conf */
930 sprintf(meetmesecs, "%i", (int) (user->jointime - time(NULL)));
931 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
935 ast_mutex_unlock(&conflock);
939 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
941 struct ast_config *cfg;
942 struct ast_variable *var;
943 struct ast_conference *cnf = confs;
945 /* Check first in the conference list */
946 ast_mutex_lock(&conflock);
948 if (!strcmp(confno, cnf->confno))
952 ast_mutex_unlock(&conflock);
956 /* No need to parse meetme.conf */
957 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
959 if (dynamic_pin[0] == 'q') {
960 /* Query the user to enter a PIN */
961 ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
963 cnf = build_conf(confno, dynamic_pin, make, dynamic);
965 cnf = build_conf(confno, "", make, dynamic);
968 /* Check the config */
969 cfg = ast_load("meetme.conf");
971 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
974 var = ast_variable_browse(cfg, "rooms");
976 if (!strcasecmp(var->name, "conf")) {
977 /* Separate the PIN */
980 if ((pin = ast_strdupa(var->value))) {
981 conf = strsep(&pin, "|,");
982 if (!strcasecmp(conf, confno)) {
983 /* Bingo it's a valid conference */
985 cnf = build_conf(confno, pin, make, dynamic);
987 cnf = build_conf(confno, "", make, dynamic);
995 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1003 static int count_exec(struct ast_channel *chan, void *data)
1005 struct localuser *u;
1007 struct ast_conference *conf;
1009 char *confnum, *localdata;
1012 if (!data || !strlen(data)) {
1013 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1016 localdata = ast_strdupa(data);
1018 confnum = strsep(&localdata,"|");
1019 conf = find_conf(chan, confnum, 0, 0, NULL);
1021 count = conf->users;
1025 if (localdata && strlen(localdata)){
1026 /* have var so load it and exit */
1027 snprintf(val,sizeof(val), "%i",count);
1028 pbx_builtin_setvar_helper(chan, localdata,val);
1030 if (chan->_state != AST_STATE_UP)
1032 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1034 LOCAL_USER_REMOVE(u);
1038 static int conf_exec(struct ast_channel *chan, void *data)
1041 struct localuser *u;
1042 char confno[AST_MAX_EXTENSION] = "";
1045 struct ast_conference *cnf;
1048 int empty = 0, empty_no_pin = 0;
1049 char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
1051 if (!data || !strlen(data)) {
1058 if (chan->_state != AST_STATE_UP)
1061 info = ast_strdupa((char *)notdata);
1064 char *tmp = strsep(&info, "|");
1065 strncpy(confno, tmp, sizeof(confno));
1066 if (strlen(confno) == 0) {
1071 inflags = strsep(&info, "|");
1073 inpin = strsep(&info, "|");
1075 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
1078 if (strchr(inflags, 'a'))
1079 confflags |= CONFFLAG_ADMIN;
1080 if (strchr(inflags, 'm'))
1081 confflags |= CONFFLAG_MONITOR;
1082 if (strchr(inflags, 'p'))
1083 confflags |= CONFFLAG_POUNDEXIT;
1084 if (strchr(inflags, 'u'))
1085 confflags |= CONFFLAG_STARMENU;
1086 if (strchr(inflags, 't'))
1087 confflags |= CONFFLAG_TALKER;
1088 if (strchr(inflags, 'q'))
1089 confflags |= CONFFLAG_QUIET;
1090 if (strchr(inflags, 'M'))
1091 confflags |= CONFFLAG_MOH;
1092 if (strchr(inflags, 'x'))
1093 confflags |= CONFFLAG_ADMINEXIT;
1094 if (strchr(inflags, 'b'))
1095 confflags |= CONFFLAG_AGI;
1096 if (strchr(inflags, 'd'))
1098 if (strchr(inflags, 'D')) {
1101 strncpy(the_pin, "q", sizeof(the_pin) - 1);
1104 if (strchr(inflags, 'e'))
1106 if (strchr(inflags, 'E')) {
1117 struct ast_config *cfg;
1118 struct ast_variable *var;
1121 memset(map, 0, sizeof(map));
1123 ast_mutex_lock(&conflock);
1126 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
1127 /* Disqualify in use conference */
1128 if (confno_int >= 0 && confno_int < 1024)
1133 ast_mutex_unlock(&conflock);
1135 /* Disqualify static conferences with pins */
1136 cfg = ast_load("meetme.conf");
1138 var = ast_variable_browse(cfg, "rooms");
1140 if (!strcasecmp(var->name, "conf")) {
1141 char *stringp = ast_strdupa(var->value);
1143 char *confno_tmp = strsep(&stringp, "|,");
1145 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
1146 if (confno_int >= 0 && confno_int < 1024) {
1147 if (stringp && empty_no_pin) {
1153 /* For static: run through the list and see if this conference is empty */
1154 ast_mutex_lock(&conflock);
1157 if (!strcmp(confno_tmp, cnf->confno)) {
1162 ast_mutex_unlock(&conflock);
1164 if ((empty_no_pin && (!stringp)) || (!empty_no_pin)) {
1165 strncpy(confno, confno_tmp, sizeof(confno) - 1);
1177 /* Select first conference number not in use */
1179 for (i=0;i<1024;i++) {
1180 if (dynamic && (!map[i])) {
1181 snprintf(confno, sizeof(confno) - 1, "%d", i);
1188 if (!strlen(confno)) {
1189 res = ast_streamfile(chan, "conf-noempty", chan->language);
1191 ast_waitstream(chan, "");
1193 if (sscanf(confno, "%d", &confno_int) == 1) {
1194 res = ast_streamfile(chan, "conf-enteringno", chan->language);
1196 ast_waitstream(chan, "");
1197 res = ast_say_digits(chan, confno_int, "", chan->language);
1202 while (allowretry && (!strlen(confno)) && (++retrycnt < 4)) {
1203 /* Prompt user for conference number */
1204 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
1206 /* Don't try to validate when we catch an error */
1212 if (strlen(confno)) {
1213 /* Check the validity of the conference */
1214 cnf = find_conf(chan, confno, 1, dynamic, the_pin);
1216 res = ast_streamfile(chan, "conf-invalid", chan->language);
1218 ast_waitstream(chan, "");
1223 if (strlen(cnf->pin)) {
1224 char pin[AST_MAX_EXTENSION];
1227 strncpy(pin, the_pin, sizeof(pin) - 1);
1230 /* Prompt user for pin if pin is required */
1231 res = ast_app_getdata(chan, "conf-getpin", pin, sizeof(pin) - 1, 0);
1234 if (!strcasecmp(pin, cnf->pin)) {
1237 /* Run the conference */
1238 res = conf_run(chan, cnf, confflags);
1241 res = ast_streamfile(chan, "conf-invalidpin", chan->language);
1243 ast_waitstream(chan, "");
1253 /* No pin required */
1256 /* Run the conference */
1257 res = conf_run(chan, cnf, confflags);
1261 } while (allowretry);
1262 /* Do the conference */
1263 LOCAL_USER_REMOVE(u);
1267 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
1268 struct ast_conf_user *user = NULL;
1269 char usrno[1024] = "";
1270 if (conf && callerident) {
1271 user = conf->firstuser;
1273 sprintf(usrno, "%i", user->user_no);
1274 if (strcmp(usrno, callerident) == 0)
1276 user = user->nextuser;
1282 static int admin_exec(struct ast_channel *chan, void *data) {
1283 /* MeetMeAdmin(confno, command, caller) */
1284 char *params, *command = NULL, *caller = NULL, *conf = NULL;
1285 struct ast_conference *cnf;
1286 struct ast_conf_user *user = NULL;
1288 ast_mutex_lock(&conflock);
1289 /* The param has the conference number the user and the command to execute */
1290 if (data && strlen(data)) {
1291 params = ast_strdupa((char *) data);
1292 conf = strsep(¶ms, "|");
1293 command = strsep(¶ms, "|");
1294 caller = strsep(¶ms, "|");
1296 ast_mutex_lock(&conflock);
1299 if (strcmp(cnf->confno, conf) == 0)
1303 ast_mutex_unlock(&conflock);
1306 user = find_user(cnf, caller);
1309 switch((int) (*command)) {
1310 case 76: /* L: Lock */
1313 case 108: /* l: Unlock */
1316 case 75: /* K: kick all users*/
1317 user = cnf->firstuser;
1319 user->adminflags |= ADMINFLAG_KICKME;
1320 if (user->nextuser) {
1321 user = user->nextuser;
1327 case 77: /* M: Mute */
1329 user->adminflags |= ADMINFLAG_MUTED;
1331 ast_log(LOG_NOTICE, "Specified User not found!");
1334 case 109: /* m: Unmute */
1335 if (user && (user->adminflags & ADMINFLAG_MUTED)) {
1336 user->adminflags ^= ADMINFLAG_MUTED;
1338 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
1341 case 107: /* k: Kick user */
1343 user->adminflags |= ADMINFLAG_KICKME;
1345 ast_log(LOG_NOTICE, "Specified User not found!");
1350 ast_log(LOG_NOTICE, "Conference Number not found\n");
1353 ast_mutex_unlock(&conflock);
1357 int unload_module(void)
1359 STANDARD_HANGUP_LOCALUSERS;
1360 ast_cli_unregister(&cli_show_confs);
1361 ast_cli_unregister(&cli_conf);
1362 ast_unregister_application(app3);
1363 ast_unregister_application(app2);
1364 return ast_unregister_application(app);
1367 int load_module(void)
1369 ast_cli_register(&cli_show_confs);
1370 ast_cli_register(&cli_conf);
1371 ast_register_application(app3, admin_exec, synopsis3, descrip3);
1372 ast_register_application(app2, count_exec, synopsis2, descrip2);
1373 return ast_register_application(app, conf_exec, synopsis, descrip);
1376 char *description(void)
1384 STANDARD_USECOUNT(res);
1390 return ASTERISK_GPL_KEY;