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 = "MeetMe conference bridge";
39 static char *app = "MeetMe";
40 static char *app2 = "MeetMeCount";
41 static char *app3 = "MeetMeAdmin";
43 static char *synopsis = "MeetMe conference bridge";
44 static char *synopsis2 = "MeetMe participant count";
45 static char *synopsis3 = "MeetMe conference Administration";
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\n"
51 "INSTALLED 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 =
78 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
79 " 'K' -- Kick all users out of conference\n"
80 " 'k' -- Kick one user out of conference\n"
81 " 'L' -- Lock conference\n"
82 " 'l' -- Unlock conference\n"
83 " 'M' -- Mute conference\n"
84 " 'm' -- Unmute conference\n"
91 static struct ast_conference {
92 char confno[AST_MAX_EXTENSION]; /* Conference */
93 int fd; /* Announcements fd */
94 int zapconf; /* Zaptel Conf # */
95 int users; /* Number of active users */
96 int markedusers; /* Number of marked users */
97 struct ast_conf_user *firstuser; /* Pointer to the first user struct */
98 struct ast_conf_user *lastuser; /* Pointer to the last user struct */
99 time_t start; /* Start time (s) */
100 int isdynamic; /* Created on the fly? */
101 int locked; /* Is the conference locked? */
102 char pin[AST_MAX_EXTENSION]; /* If protected by a PIN */
103 struct ast_conference *next;
106 struct ast_conf_user {
107 int user_no; /* User Number */
108 struct ast_conf_user *prevuser; /* Pointer to the previous user */
109 struct ast_conf_user *nextuser; /* Pointer to the next user */
110 int userflags; /* Flags as set in the conference */
111 int adminflags; /* Flags set by the Admin */
112 struct ast_channel *chan; /* Connected channel */
113 char usrvalue[50]; /* Custom User Value */
114 time_t jointime; /* Time the user joined the conference */
117 #define ADMINFLAG_MUTED (1 << 1) /* User is muted */
118 #define ADMINFLAG_KICKME (1 << 2) /* User is kicked */
121 static ast_mutex_t conflock = AST_MUTEX_INITIALIZER;
123 static int admin_exec(struct ast_channel *chan, void *data);
131 #define CONF_SIZE 160
133 #define CONFFLAG_ADMIN (1 << 1) /* If set the user has admin access on the conference */
134 #define CONFFLAG_MONITOR (1 << 2) /* If set the user can only receive audio from the conference */
135 #define CONFFLAG_POUNDEXIT (1 << 3) /* If set asterisk will exit conference when '#' is pressed */
136 #define CONFFLAG_STARMENU (1 << 4) /* If set asterisk will provide a menu to the user what '*' is pressed */
137 #define CONFFLAG_TALKER (1 << 5) /* If set the use can only send audio to the conference */
138 #define CONFFLAG_QUIET (1 << 6) /* If set there will be no enter or leave sounds */
139 #define CONFFLAG_VIDEO (1 << 7) /* Set to enable video mode */
140 #define CONFFLAG_AGI (1 << 8) /* Set to run AGI Script in Background */
141 #define CONFFLAG_MOH (1 << 9) /* Set to have music on hold when user is alone in conference */
142 #define CONFFLAG_ADMINEXIT (1 << 10) /* If set the MeetMe will return if all marked with this flag left */
145 static int careful_write(int fd, unsigned char *data, int len)
149 res = write(fd, data, len);
151 if (errno != EAGAIN) {
152 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
163 static void conf_play(struct ast_conference *conf, int sound)
167 ast_mutex_lock(&conflock);
182 careful_write(conf->fd, data, len);
183 ast_mutex_unlock(&conflock);
186 static struct ast_conference *build_conf(char *confno, char *pin, int make, int dynamic)
188 struct ast_conference *cnf;
189 struct zt_confinfo ztc;
190 ast_mutex_lock(&conflock);
193 if (!strcmp(confno, cnf->confno))
197 if (!cnf && (make || dynamic)) {
198 cnf = malloc(sizeof(struct ast_conference));
201 memset(cnf, 0, sizeof(struct ast_conference));
202 strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1);
203 strncpy(cnf->pin, pin, sizeof(cnf->pin) - 1);
204 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
206 ast_log(LOG_WARNING, "Unable to open pseudo channel\n");
211 memset(&ztc, 0, sizeof(ztc));
212 /* Setup a new zap conference */
215 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
216 if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
217 ast_log(LOG_WARNING, "Error setting conference\n");
223 /* Fill the conference struct */
224 cnf->start = time(NULL);
225 cnf->zapconf = ztc.confno;
226 cnf->isdynamic = dynamic;
227 cnf->firstuser = NULL;
228 cnf->lastuser = NULL;
230 if (option_verbose > 2)
231 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
235 ast_log(LOG_WARNING, "Out of memory\n");
238 ast_mutex_unlock(&conflock);
242 static int confs_show(int fd, int argc, char **argv)
244 ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
245 return RESULT_SUCCESS;
248 static char show_confs_usage[] =
249 "Deprecated! Please use 'meetme' instead.\n";
251 static struct ast_cli_entry cli_show_confs = {
252 { "show", "conferences", NULL }, confs_show,
253 "Show status of conferences", show_confs_usage, NULL };
255 static int conf_cmd(int fd, int argc, char **argv) {
256 /* Process the command */
257 struct ast_conference *cnf;
258 struct ast_conf_user *user;
260 int i = 0, total = 0;
262 char *header_format = "%-14s %-14s %-8s %-8s\n";
263 char *data_format = "%-12.12s %4.4d %02d:%02d:%02d %-8s\n";
264 char cmdline[1024] = "";
267 ast_cli(fd, "Invalid Arguments.\n");
268 /* Check for length so no buffer will overflow... */
269 for (i = 0; i < argc; i++) {
270 if (strlen(argv[i]) > 100)
271 ast_cli(fd, "Invalid Arguments.\n");
274 /* 'MeetMe': List all the conferences */
278 ast_cli(fd, "No active MeetMe conferences.\n");
279 return RESULT_SUCCESS;
281 ast_cli(fd, header_format, "Conf Num", "Parties", "Activity", "Creation");
283 hr = (now - cnf->start) / 3600;
284 min = ((now - cnf->start) % 3600) / 60;
285 sec = (now - cnf->start) % 60;
288 ast_cli(fd, data_format, cnf->confno, cnf->users, hr, min, sec, "Dynamic");
290 ast_cli(fd, data_format, cnf->confno, cnf->users, hr, min, sec, "Static");
295 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
296 return RESULT_SUCCESS;
299 return RESULT_SHOWUSAGE;
300 strncpy(cmdline, argv[2], 100); /* Argv 2: conference number */
301 if (strstr(argv[1], "lock")) {
302 if (strcmp(argv[1], "lock") == 0) {
304 strcat(cmdline, "|L");
307 strcat(cmdline, "|l");
309 } else if (strstr(argv[1], "mute")) {
311 return RESULT_SHOWUSAGE;
312 if (strcmp(argv[1], "mute") == 0) {
314 strcat(cmdline, "|M|");
315 strcat(cmdline, argv[3]);
318 strcat(cmdline, "|m|");
319 strcat(cmdline, argv[3]);
321 } else if (strcmp(argv[1], "kick") == 0) {
323 return RESULT_SHOWUSAGE;
324 if (strcmp(argv[3], "all") == 0) {
326 strcat(cmdline, "|K");
328 /* Kick a single user */
329 strcat(cmdline, "|k|");
330 strcat(cmdline, argv[3]);
332 } else if(strcmp(argv[1], "list") == 0) {
333 /* List all the users in a conference */
335 ast_cli(fd, "No active conferences.\n");
336 return RESULT_SUCCESS;
339 /* Find the right conference */
341 if (strcmp(cnf->confno, argv[2]) == 0)
346 ast_cli(fd, "No such conference: %s.\n",argv[2]);
347 return RESULT_SUCCESS;
350 /* Show all the users */
351 user = cnf->firstuser;
353 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)" : "" );
354 user = user->nextuser;
356 return RESULT_SUCCESS;
358 return RESULT_SHOWUSAGE;
359 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
360 admin_exec(NULL, cmdline);
364 static char *complete_confcmd(char *line, char *word, int pos, int state) {
365 #define CONF_COMMANDS 6
366 int which = 0, x = 0;
367 struct ast_conference *cnf = NULL;
368 struct ast_conf_user *usr = NULL;
371 char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"};
376 for (x = 0;x < CONF_COMMANDS; x++) {
377 if (!strncasecmp(cmds[x], word, strlen(word))) {
378 if (++which > state) {
379 return strdup(cmds[x]);
383 } else if (pos == 2) {
384 /* Conference Number */
385 ast_mutex_lock(&conflock);
388 if (!strncasecmp(word, cnf->confno, strlen(word))) {
394 ast_mutex_unlock(&conflock);
395 return cnf ? strdup(cnf->confno) : NULL;
396 } else if (pos == 3) {
397 /* User Number || Conf Command option*/
398 if (strstr(line, "mute") || strstr(line, "kick")) {
399 if ((state == 0) && (strstr(line, "kick")) && !(strncasecmp(word, "all", strlen(word)))) {
400 return strdup("all");
403 ast_mutex_lock(&conflock);
406 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
407 myline = strdupa(line);
408 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
409 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
414 if (strcmp(confno, cnf->confno) == 0) {
420 /* Search for the user */
421 usr = cnf->firstuser;
423 sprintf(usrno, "%i", usr->user_no);
424 if (!strncasecmp(word, usrno, strlen(word))) {
431 ast_mutex_unlock(&conflock);
432 return usr ? strdup(usrno) : NULL;
438 static char conf_usage[] =
439 "Usage: meetme (un)lock|(un)mute|kick|list <confno> <usernumber>\n"
440 " Executes a command for the conference or on a conferee\n";
442 static struct ast_cli_entry cli_conf = {
443 { "meetme", NULL, NULL }, conf_cmd,
444 "Execute a command on a conference or conferee", conf_usage, complete_confcmd };
446 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
448 struct ast_conference *prev=NULL, *cur;
449 struct ast_conf_user *user = malloc(sizeof(struct ast_conf_user));
451 struct zt_confinfo ztc;
453 struct ast_channel *c;
470 char *agifiledefault = "conf-background.agi";
474 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
475 char *buf = __buf + AST_FRIENDLY_OFFSET;
477 user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */
480 /* Sorry, but this confernce is locked! */
481 if (!ast_streamfile(chan, "conf-locked", chan->language))
482 ast_waitstream(chan, "");
487 if (confflags & CONFFLAG_ADMINEXIT) {
488 if (conf->markedusers == -1) {
489 conf->markedusers = 1;
495 ast_mutex_lock(&conflock);
496 if (conf->firstuser == NULL) {
497 /* Fill the first new User struct */
499 user->nextuser = NULL;
500 user->prevuser = NULL;
501 conf->firstuser = user;
502 conf->lastuser = user;
504 /* Fill the new user struct */
505 user->user_no = conf->lastuser->user_no + 1;
506 user->prevuser = conf->lastuser;
507 user->nextuser = NULL;
508 if (conf->lastuser->nextuser != NULL) {
509 ast_log(LOG_WARNING, "Error in User Management!\n");
512 conf->lastuser->nextuser = user;
513 conf->lastuser = user;
516 strncpy(user->usrvalue, "test", sizeof(user->usrvalue));
518 user->userflags = confflags;
519 user->adminflags = 0;
520 ast_mutex_unlock(&conflock);
522 if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
523 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
524 ast_waitstream(chan, "");
527 if (confflags & CONFFLAG_VIDEO) {
528 /* Set it into linear mode (write) */
529 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
530 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
534 /* Set it into linear mode (read) */
535 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
536 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
540 /* Set it into U-law mode (write) */
541 if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
542 ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
546 /* Set it into U-law mode (read) */
547 if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
548 ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
552 ast_indicate(chan, -1);
553 retryzap = strcasecmp(chan->type, "Zap");
555 origfd = chan->fds[0];
557 fd = open("/dev/zap/pseudo", O_RDWR);
559 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
562 /* Make non-blocking */
563 flags = fcntl(fd, F_GETFL);
565 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
569 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
570 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
574 /* Setup buffering information */
575 memset(&bi, 0, sizeof(bi));
576 bi.bufsize = CONF_SIZE;
577 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
578 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
580 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
581 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
585 if (confflags & CONFFLAG_VIDEO) {
587 if (ioctl(fd, ZT_SETLINEAR, &x)) {
588 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
595 /* XXX Make sure we're not running on a pseudo channel XXX */
599 memset(&ztc, 0, sizeof(ztc));
600 /* Check to see if we're in a conference... */
602 if (ioctl(fd, ZT_GETCONF, &ztc)) {
603 ast_log(LOG_WARNING, "Error getting conference\n");
608 /* Whoa, already in a conference... Retry... */
610 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
615 memset(&ztc, 0, sizeof(ztc));
616 /* Add us to the conference */
618 ztc.confno = conf->zapconf;
619 if (confflags & CONFFLAG_MONITOR)
620 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
621 else if (confflags & CONFFLAG_TALKER)
622 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
624 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
626 if (ioctl(fd, ZT_SETCONF, &ztc)) {
627 ast_log(LOG_WARNING, "Error setting conference\n");
631 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
633 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
637 chan->name, chan->uniqueid, conf->confno);
639 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
641 if (!(confflags & CONFFLAG_QUIET))
642 conf_play(conf, ENTER);
645 if (confflags & CONFFLAG_AGI) {
647 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
648 or use default filename of conf-background.agi */
650 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
652 agifile = agifiledefault;
654 if (!strcasecmp(chan->type,"Zap")) {
655 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
657 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
659 /* Find a pointer to the agi app and execute the script */
660 app = pbx_findapp("agi");
662 ret = pbx_exec(chan, app, agifile, 1);
664 ast_log(LOG_WARNING, "Could not find application (agi)\n");
667 if (!strcasecmp(chan->type,"Zap")) {
668 /* Remove CONFMUTE mode on Zap channel */
670 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
673 if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
674 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
676 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
681 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
683 /* Update the struct with the actual confflags */
684 user->userflags = confflags;
686 /* trying to add moh for single person conf */
687 if (confflags & CONFFLAG_MOH) {
688 if (conf->users == 1) {
689 if (musiconhold == 0) {
690 ast_moh_start(chan, NULL);
701 /* Leave if the last marked user left */
702 if ((confflags & CONFFLAG_ADMINEXIT) && (conf->markedusers == 0)) {
707 /* Check if the admin changed my modes */
708 if (user->adminflags) {
709 /* Set the new modes */
710 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
711 ztc.confmode ^= ZT_CONF_TALKER;
712 if (ioctl(fd, ZT_SETCONF, &ztc)) {
713 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
718 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
719 ztc.confmode |= ZT_CONF_TALKER;
720 if (ioctl(fd, ZT_SETCONF, &ztc)) {
721 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
726 if (user->adminflags & ADMINFLAG_KICKME) {
727 //You have been kicked.
728 if (!ast_streamfile(chan, "conf-kicked", chan->language))
729 ast_waitstream(chan, "");
733 } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
734 ztc.confmode |= ZT_CONF_TALKER;
735 if (ioctl(fd, ZT_SETCONF, &ztc)) {
736 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
743 if (c->fds[0] != origfd) {
745 /* Kill old pseudo */
748 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
755 if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
758 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
762 if ((confflags & CONFFLAG_ADMIN)) {
766 /* Record this sound! */
767 if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
768 ast_waitstream(chan, "");
770 switch(f->subclass - 48) {
771 case 1: /* Un/Mute */
773 if (ztc.confmode & ZT_CONF_TALKER) {
774 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
775 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
777 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
778 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
780 if (ioctl(fd, ZT_SETCONF, &ztc)) {
781 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
785 if (ztc.confmode & ZT_CONF_TALKER) {
786 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
787 ast_waitstream(chan, "");
789 if (!ast_streamfile(chan, "conf-muted", chan->language))
790 ast_waitstream(chan, "");
793 case 2: /* Un/Lock the Conference */
797 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
798 ast_waitstream(chan, "");
801 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
802 ast_waitstream(chan, "");
808 /* Play an error message! */
809 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
810 ast_waitstream(chan, "");
818 /* Record this sound! */
819 if (!ast_streamfile(chan, "conf-usermenu", chan->language))
820 ast_waitstream(chan, "");
822 switch(f->subclass - 48) {
823 case 1: /* Un/Mute */
825 if (ztc.confmode & ZT_CONF_TALKER) {
826 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
827 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
828 } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
829 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
830 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
832 if (ioctl(fd, ZT_SETCONF, &ztc)) {
833 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
837 if (ztc.confmode & ZT_CONF_TALKER) {
838 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
839 ast_waitstream(chan, "");
841 if (!ast_streamfile(chan, "conf-muted", chan->language))
842 ast_waitstream(chan, "");
847 /* Play an error message! */
848 if (!ast_streamfile(chan, "errormenu", chan->language))
849 ast_waitstream(chan, "");
855 ast_moh_start(chan, NULL);
857 } else if (fd != chan->fds[0]) {
858 if (f->frametype == AST_FRAME_VOICE) {
859 if (f->subclass == AST_FORMAT_ULAW) {
860 /* Carefully write */
861 careful_write(fd, f->data, f->datalen);
863 ast_log(LOG_WARNING, "Huh? Got a non-ulaw (%d) frame in the conference\n", f->subclass);
867 } else if (outfd > -1) {
868 res = read(outfd, buf, CONF_SIZE);
870 memset(&fr, 0, sizeof(fr));
871 fr.frametype = AST_FRAME_VOICE;
872 fr.subclass = AST_FORMAT_ULAW;
876 fr.offset = AST_FRIENDLY_OFFSET;
877 if (ast_write(chan, &fr) < 0) {
878 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
882 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
886 if (fd != chan->fds[0])
889 /* Take out of conference */
890 /* Add us to the conference */
894 if (ioctl(fd, ZT_SETCONF, &ztc)) {
895 ast_log(LOG_WARNING, "Error setting conference\n");
898 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
899 conf_play(conf, LEAVE);
902 if (user->user_no) { /* Only cleanup users who really joined! */
903 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
907 chan->name, chan->uniqueid, conf->confno);
908 ast_mutex_lock(&conflock);
912 /* No more users -- close this one out */
916 prev->next = conf->next;
925 ast_log(LOG_WARNING, "Conference not found\n");
929 /* Remove the user struct */
930 if (user == cur->firstuser) {
931 cur->firstuser->nextuser->prevuser = NULL;
932 cur->firstuser = cur->firstuser->nextuser;
933 } else if (user == cur->lastuser){
934 cur->lastuser->prevuser->nextuser = NULL;
935 cur->lastuser = cur->lastuser->prevuser;
937 user->nextuser->prevuser = user->prevuser;
938 user->prevuser->nextuser = user->nextuser;
940 /* Return the number of seconds the user was in the conf */
941 sprintf(meetmesecs, "%i", (int) (user->jointime - time(NULL)));
942 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
946 ast_mutex_unlock(&conflock);
950 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
952 struct ast_config *cfg;
953 struct ast_variable *var;
954 struct ast_conference *cnf = confs;
956 /* Check first in the conference list */
957 ast_mutex_lock(&conflock);
959 if (!strcmp(confno, cnf->confno))
963 ast_mutex_unlock(&conflock);
967 /* No need to parse meetme.conf */
968 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
970 if (dynamic_pin[0] == 'q') {
971 /* Query the user to enter a PIN */
972 ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
974 cnf = build_conf(confno, dynamic_pin, make, dynamic);
976 cnf = build_conf(confno, "", make, dynamic);
979 /* Check the config */
980 cfg = ast_load("meetme.conf");
982 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
985 var = ast_variable_browse(cfg, "rooms");
987 if (!strcasecmp(var->name, "conf")) {
988 /* Separate the PIN */
991 if ((pin = ast_strdupa(var->value))) {
992 conf = strsep(&pin, "|,");
993 if (!strcasecmp(conf, confno)) {
994 /* Bingo it's a valid conference */
996 cnf = build_conf(confno, pin, make, dynamic);
998 cnf = build_conf(confno, "", make, dynamic);
1006 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1014 /*--- count_exec: The MeetmeCount application */
1015 static int count_exec(struct ast_channel *chan, void *data)
1017 struct localuser *u;
1019 struct ast_conference *conf;
1021 char *confnum, *localdata;
1024 if (!data || !strlen(data)) {
1025 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1028 localdata = ast_strdupa(data);
1030 confnum = strsep(&localdata,"|");
1031 conf = find_conf(chan, confnum, 0, 0, NULL);
1033 count = conf->users;
1037 if (localdata && strlen(localdata)){
1038 /* have var so load it and exit */
1039 snprintf(val,sizeof(val), "%i",count);
1040 pbx_builtin_setvar_helper(chan, localdata,val);
1042 if (chan->_state != AST_STATE_UP)
1044 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1046 LOCAL_USER_REMOVE(u);
1050 /*--- conf_exec: The meetme() application */
1051 static int conf_exec(struct ast_channel *chan, void *data)
1054 struct localuser *u;
1055 char confno[AST_MAX_EXTENSION] = "";
1058 struct ast_conference *cnf;
1061 int empty = 0, empty_no_pin = 0;
1062 char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
1064 if (!data || !strlen(data)) {
1071 if (chan->_state != AST_STATE_UP)
1074 info = ast_strdupa((char *)notdata);
1077 char *tmp = strsep(&info, "|");
1078 strncpy(confno, tmp, sizeof(confno));
1079 if (strlen(confno) == 0) {
1084 inflags = strsep(&info, "|");
1086 inpin = strsep(&info, "|");
1088 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
1091 if (strchr(inflags, 'a'))
1092 confflags |= CONFFLAG_ADMIN;
1093 if (strchr(inflags, 'm'))
1094 confflags |= CONFFLAG_MONITOR;
1095 if (strchr(inflags, 'p'))
1096 confflags |= CONFFLAG_POUNDEXIT;
1097 if (strchr(inflags, 'u'))
1098 confflags |= CONFFLAG_STARMENU;
1099 if (strchr(inflags, 't'))
1100 confflags |= CONFFLAG_TALKER;
1101 if (strchr(inflags, 'q'))
1102 confflags |= CONFFLAG_QUIET;
1103 if (strchr(inflags, 'M'))
1104 confflags |= CONFFLAG_MOH;
1105 if (strchr(inflags, 'x'))
1106 confflags |= CONFFLAG_ADMINEXIT;
1107 if (strchr(inflags, 'b'))
1108 confflags |= CONFFLAG_AGI;
1109 if (strchr(inflags, 'd'))
1111 if (strchr(inflags, 'D')) {
1114 strncpy(the_pin, "q", sizeof(the_pin) - 1);
1117 if (strchr(inflags, 'e'))
1119 if (strchr(inflags, 'E')) {
1130 struct ast_config *cfg;
1131 struct ast_variable *var;
1134 memset(map, 0, sizeof(map));
1136 ast_mutex_lock(&conflock);
1139 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
1140 /* Disqualify in use conference */
1141 if (confno_int >= 0 && confno_int < 1024)
1146 ast_mutex_unlock(&conflock);
1148 /* Disqualify static conferences with pins */
1149 cfg = ast_load("meetme.conf");
1151 var = ast_variable_browse(cfg, "rooms");
1153 if (!strcasecmp(var->name, "conf")) {
1154 char *stringp = ast_strdupa(var->value);
1156 char *confno_tmp = strsep(&stringp, "|,");
1158 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
1159 if (confno_int >= 0 && confno_int < 1024) {
1160 if (stringp && empty_no_pin) {
1166 /* For static: run through the list and see if this conference is empty */
1167 ast_mutex_lock(&conflock);
1170 if (!strcmp(confno_tmp, cnf->confno)) {
1175 ast_mutex_unlock(&conflock);
1177 if ((empty_no_pin && (!stringp)) || (!empty_no_pin)) {
1178 strncpy(confno, confno_tmp, sizeof(confno) - 1);
1190 /* Select first conference number not in use */
1192 for (i=0;i<1024;i++) {
1193 if (dynamic && (!map[i])) {
1194 snprintf(confno, sizeof(confno) - 1, "%d", i);
1201 if (!strlen(confno)) {
1202 res = ast_streamfile(chan, "conf-noempty", chan->language);
1204 ast_waitstream(chan, "");
1206 if (sscanf(confno, "%d", &confno_int) == 1) {
1207 res = ast_streamfile(chan, "conf-enteringno", chan->language);
1209 ast_waitstream(chan, "");
1210 res = ast_say_digits(chan, confno_int, "", chan->language);
1215 while (allowretry && (!strlen(confno)) && (++retrycnt < 4)) {
1216 /* Prompt user for conference number */
1217 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
1219 /* Don't try to validate when we catch an error */
1225 if (strlen(confno)) {
1226 /* Check the validity of the conference */
1227 cnf = find_conf(chan, confno, 1, dynamic, the_pin);
1229 res = ast_streamfile(chan, "conf-invalid", chan->language);
1231 ast_waitstream(chan, "");
1236 if (strlen(cnf->pin)) {
1237 char pin[AST_MAX_EXTENSION];
1240 strncpy(pin, the_pin, sizeof(pin) - 1);
1243 /* Prompt user for pin if pin is required */
1244 res = ast_app_getdata(chan, "conf-getpin", pin, sizeof(pin) - 1, 0);
1247 if (!strcasecmp(pin, cnf->pin)) {
1250 /* Run the conference */
1251 res = conf_run(chan, cnf, confflags);
1254 res = ast_streamfile(chan, "conf-invalidpin", chan->language);
1256 ast_waitstream(chan, "");
1266 /* No pin required */
1269 /* Run the conference */
1270 res = conf_run(chan, cnf, confflags);
1274 } while (allowretry);
1275 /* Do the conference */
1276 LOCAL_USER_REMOVE(u);
1280 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
1281 struct ast_conf_user *user = NULL;
1282 char usrno[1024] = "";
1283 if (conf && callerident) {
1284 user = conf->firstuser;
1286 sprintf(usrno, "%i", user->user_no);
1287 if (strcmp(usrno, callerident) == 0)
1289 user = user->nextuser;
1295 /*--- admin_exec: The MeetMeadmin application */
1296 /* MeetMeAdmin(confno, command, caller) */
1297 static int admin_exec(struct ast_channel *chan, void *data) {
1298 char *params, *command = NULL, *caller = NULL, *conf = NULL;
1299 struct ast_conference *cnf;
1300 struct ast_conf_user *user = NULL;
1302 ast_mutex_lock(&conflock);
1303 /* The param has the conference number the user and the command to execute */
1304 if (data && strlen(data)) {
1305 params = ast_strdupa((char *) data);
1306 conf = strsep(¶ms, "|");
1307 command = strsep(¶ms, "|");
1308 caller = strsep(¶ms, "|");
1310 ast_mutex_lock(&conflock);
1313 if (strcmp(cnf->confno, conf) == 0)
1317 ast_mutex_unlock(&conflock);
1320 user = find_user(cnf, caller);
1323 switch((int) (*command)) {
1324 case 76: /* L: Lock */
1327 case 108: /* l: Unlock */
1330 case 75: /* K: kick all users*/
1331 user = cnf->firstuser;
1333 user->adminflags |= ADMINFLAG_KICKME;
1334 if (user->nextuser) {
1335 user = user->nextuser;
1341 case 77: /* M: Mute */
1343 user->adminflags |= ADMINFLAG_MUTED;
1345 ast_log(LOG_NOTICE, "Specified User not found!");
1348 case 109: /* m: Unmute */
1349 if (user && (user->adminflags & ADMINFLAG_MUTED)) {
1350 user->adminflags ^= ADMINFLAG_MUTED;
1352 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
1355 case 107: /* k: Kick user */
1357 user->adminflags |= ADMINFLAG_KICKME;
1359 ast_log(LOG_NOTICE, "Specified User not found!");
1364 ast_log(LOG_NOTICE, "Conference Number not found\n");
1367 ast_mutex_unlock(&conflock);
1371 int unload_module(void)
1373 STANDARD_HANGUP_LOCALUSERS;
1374 ast_cli_unregister(&cli_show_confs);
1375 ast_cli_unregister(&cli_conf);
1376 ast_unregister_application(app3);
1377 ast_unregister_application(app2);
1378 return ast_unregister_application(app);
1381 int load_module(void)
1383 ast_cli_register(&cli_show_confs);
1384 ast_cli_register(&cli_conf);
1385 ast_register_application(app3, admin_exec, synopsis3, descrip3);
1386 ast_register_application(app2, count_exec, synopsis2, descrip2);
1387 return ast_register_application(app, conf_exec, synopsis, descrip);
1390 char *description(void)
1398 STANDARD_USECOUNT(res);
1404 return ASTERISK_GPL_KEY;