2 * Asterisk -- A telephony toolkit for Linux.
4 * Meet me conference bridge
6 * Copyright (C) 1999 - 2005, Digium, Inc.
8 * Mark Spencer <markster@digium.com>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/pbx.h>
19 #include <asterisk/module.h>
20 #include <asterisk/config.h>
21 #include <asterisk/app.h>
22 #include <asterisk/dsp.h>
23 #include <asterisk/musiconhold.h>
24 #include <asterisk/manager.h>
25 #include <asterisk/options.h>
26 #include <asterisk/cli.h>
27 #include <asterisk/say.h>
28 #include <asterisk/utils.h>
34 #include <sys/ioctl.h>
35 #include "../asterisk.h"
36 #include "../astconf.h"
39 #include <linux/zaptel.h>
42 #endif /* __linux__ */
44 static char *tdesc = "MeetMe conference bridge";
46 static char *app = "MeetMe";
47 static char *app2 = "MeetMeCount";
48 static char *app3 = "MeetMeAdmin";
50 static char *synopsis = "MeetMe conference bridge";
51 static char *synopsis2 = "MeetMe participant count";
52 static char *synopsis3 = "MeetMe conference Administration";
54 static char *descrip =
55 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe conference.\n"
56 "If the conference number is omitted, the user will be prompted to enter\n"
58 "MeetMe returns 0 if user pressed # to exit (see option 'p'), otherwise -1.\n"
59 "Please note: A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING TO WORK!\n\n"
61 "The option string may contain zero or more of the following characters:\n"
62 " 'm' -- set monitor only mode (Listen only, no talking)\n"
63 " 't' -- set talk only mode. (Talk only, no listening)\n"
64 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
65 " 'i' -- announce user join/leave\n"
66 " 'p' -- allow user to exit the conference by pressing '#'\n"
67 " 'X' -- allow user to exit the conference by entering a valid single\n"
68 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
69 " if that variable is not defined.\n"
70 " 'd' -- dynamically add conference\n"
71 " 'D' -- dynamically add conference, prompting for a PIN\n"
72 " 'e' -- select an empty conference\n"
73 " 'E' -- select an empty pinless conference\n"
74 " 'v' -- video mode\n"
75 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
76 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
77 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is wav.\n"
78 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
79 " 'M' -- enable music on hold when the conference has a single caller\n"
80 " 'x' -- close the conference when last marked user exits\n"
81 " 'w' -- wait until the marked user enters the conference\n"
82 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
83 " Default: conf-background.agi\n"
84 " (Note: This does not work with non-Zap channels in the same conference)\n"
85 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
86 " 'a' -- set admin mode\n"
87 " 'A' -- set marked mode\n"
88 " 'P' -- always prompt for the pin even if it is specified\n";
90 static char *descrip2 =
91 " MeetMeCount(confno[|var]): Plays back the number of users in the specifiedi\n"
92 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
93 "will be returned in the variable. Returns 0 on success or -1 on a hangup.\n"
94 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
96 static char *descrip3 =
97 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
98 " 'K' -- Kick all users out of conference\n"
99 " 'k' -- Kick one user out of conference\n"
100 " 'e' -- Eject last user that joined\n"
101 " 'L' -- Lock conference\n"
102 " 'l' -- Unlock conference\n"
103 " 'M' -- Mute conference\n"
104 " 'm' -- Unmute conference\n"
105 " 'N' -- Mute entire conference (except admin)\n"
106 " 'n' -- Unmute entire conference (except admin)\n"
113 static struct ast_conference {
114 char confno[AST_MAX_EXTENSION]; /* Conference */
115 struct ast_channel *chan; /* Announcements channel */
116 int fd; /* Announcements fd */
117 int zapconf; /* Zaptel Conf # */
118 int users; /* Number of active users */
119 int markedusers; /* Number of marked users */
120 struct ast_conf_user *firstuser; /* Pointer to the first user struct */
121 struct ast_conf_user *lastuser; /* Pointer to the last user struct */
122 time_t start; /* Start time (s) */
123 int recording; /* recording status */
124 int isdynamic; /* Created on the fly? */
125 int locked; /* Is the conference locked? */
126 pthread_t recordthread; /* thread for recording */
127 pthread_attr_t attr; /* thread attribute */
128 char *recordingfilename; /* Filename to record the Conference into */
129 char *recordingformat; /* Format to record the Conference in */
130 char pin[AST_MAX_EXTENSION]; /* If protected by a PIN */
131 char pinadmin[AST_MAX_EXTENSION]; /* If protected by a admin PIN */
132 struct ast_conference *next;
135 struct ast_conf_user {
136 int user_no; /* User Number */
137 struct ast_conf_user *prevuser; /* Pointer to the previous user */
138 struct ast_conf_user *nextuser; /* Pointer to the next user */
139 int userflags; /* Flags as set in the conference */
140 int adminflags; /* Flags set by the Admin */
141 struct ast_channel *chan; /* Connected channel */
142 int talking; /* Is user talking */
143 char usrvalue[50]; /* Custom User Value */
144 char namerecloc[AST_MAX_EXTENSION]; /* Name Recorded file Location */
145 time_t jointime; /* Time the user joined the conference */
148 #define ADMINFLAG_MUTED (1 << 1) /* User is muted */
149 #define ADMINFLAG_KICKME (1 << 2) /* User is kicked */
150 #define MEETME_DELAYDETECTTALK 300
151 #define MEETME_DELAYDETECTENDTALK 1000
153 AST_MUTEX_DEFINE_STATIC(conflock);
155 static int admin_exec(struct ast_channel *chan, void *data);
157 static void *recordthread(void *args);
165 #define MEETME_RECORD_OFF 0
166 #define MEETME_RECORD_ACTIVE 1
167 #define MEETME_RECORD_TERMINATE 2
169 #define CONF_SIZE 320
171 #define CONFFLAG_ADMIN (1 << 1) /* If set the user has admin access on the conference */
172 #define CONFFLAG_MONITOR (1 << 2) /* If set the user can only receive audio from the conference */
173 #define CONFFLAG_POUNDEXIT (1 << 3) /* If set asterisk will exit conference when '#' is pressed */
174 #define CONFFLAG_STARMENU (1 << 4) /* If set asterisk will provide a menu to the user what '*' is pressed */
175 #define CONFFLAG_TALKER (1 << 5) /* If set the use can only send audio to the conference */
176 #define CONFFLAG_QUIET (1 << 6) /* If set there will be no enter or leave sounds */
177 #define CONFFLAG_VIDEO (1 << 7) /* Set to enable video mode */
178 #define CONFFLAG_AGI (1 << 8) /* Set to run AGI Script in Background */
179 #define CONFFLAG_MOH (1 << 9) /* Set to have music on hold when user is alone in conference */
180 #define CONFFLAG_MARKEDEXIT (1 << 10) /* If set the MeetMe will return if all marked with this flag left */
181 #define CONFFLAG_WAITMARKED (1 << 11) /* If set, the MeetMe will wait until a marked user enters */
182 #define CONFFLAG_EXIT_CONTEXT (1 << 12) /* If set, the MeetMe will exit to the specified context */
183 #define CONFFLAG_MARKEDUSER (1 << 13) /* If set, the user will be marked */
184 #define CONFFLAG_INTROUSER (1 << 14) /* If set, user will be ask record name on entry of conference */
185 #define CONFFLAG_RECORDCONF (1<< 15) /* If set, the MeetMe will be recorded */
186 #define CONFFLAG_MONITORTALKER (1 << 16) /* If set, the user will be monitored if the user is talking or not */
188 static char *istalking(int x)
193 return "(unmonitored)";
195 return "(not talking)";
198 static int careful_write(int fd, unsigned char *data, int len)
203 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
204 res = ioctl(fd, ZT_IOMUX, &x);
206 res = write(fd, data, len);
208 if (errno != EAGAIN) {
209 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
220 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, int sound)
225 if (!chan->_softhangup)
226 res = ast_autoservice_start(chan);
227 ast_mutex_lock(&conflock);
242 careful_write(conf->fd, data, len);
243 ast_mutex_unlock(&conflock);
245 ast_autoservice_stop(chan);
248 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic)
250 struct ast_conference *cnf;
251 struct zt_confinfo ztc;
252 ast_mutex_lock(&conflock);
255 if (!strcmp(confno, cnf->confno))
259 if (!cnf && (make || dynamic)) {
260 cnf = malloc(sizeof(struct ast_conference));
263 memset(cnf, 0, sizeof(struct ast_conference));
264 strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1);
265 strncpy(cnf->pin, pin, sizeof(cnf->pin) - 1);
266 strncpy(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin) - 1);
267 cnf->markedusers = 0;
268 cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo", NULL);
270 cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */
272 ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
273 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
275 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
281 memset(&ztc, 0, sizeof(ztc));
282 /* Setup a new zap conference */
285 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
286 if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
287 ast_log(LOG_WARNING, "Error setting conference\n");
289 ast_hangup(cnf->chan);
296 /* Fill the conference struct */
297 cnf->start = time(NULL);
298 cnf->zapconf = ztc.confno;
299 cnf->isdynamic = dynamic;
300 cnf->firstuser = NULL;
301 cnf->lastuser = NULL;
303 if (option_verbose > 2)
304 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
308 ast_log(LOG_WARNING, "Out of memory\n");
311 ast_mutex_unlock(&conflock);
315 static int confs_show(int fd, int argc, char **argv)
317 ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
318 return RESULT_SUCCESS;
321 static char show_confs_usage[] =
322 "Deprecated! Please use 'meetme' instead.\n";
324 static struct ast_cli_entry cli_show_confs = {
325 { "show", "conferences", NULL }, confs_show,
326 "Show status of conferences", show_confs_usage, NULL };
328 static int conf_cmd(int fd, int argc, char **argv) {
329 /* Process the command */
330 struct ast_conference *cnf;
331 struct ast_conf_user *user;
333 int i = 0, total = 0;
335 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
336 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
337 char cmdline[1024] = "";
340 ast_cli(fd, "Invalid Arguments.\n");
341 /* Check for length so no buffer will overflow... */
342 for (i = 0; i < argc; i++) {
343 if (strlen(argv[i]) > 100)
344 ast_cli(fd, "Invalid Arguments.\n");
347 /* 'MeetMe': List all the conferences */
351 ast_cli(fd, "No active MeetMe conferences.\n");
352 return RESULT_SUCCESS;
354 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
356 if (cnf->markedusers == 0)
357 strncpy(cmdline, "N/A ", sizeof(cmdline) - 1);
359 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
360 hr = (now - cnf->start) / 3600;
361 min = ((now - cnf->start) % 3600) / 60;
362 sec = (now - cnf->start) % 60;
364 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
369 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
370 return RESULT_SUCCESS;
373 return RESULT_SHOWUSAGE;
374 strncpy(cmdline, argv[2], sizeof(cmdline) - 1); /* Argv 2: conference number */
375 if (strstr(argv[1], "lock")) {
376 if (strcmp(argv[1], "lock") == 0) {
378 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
381 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
383 } else if (strstr(argv[1], "mute")) {
385 return RESULT_SHOWUSAGE;
386 if (strcmp(argv[1], "mute") == 0) {
388 if (strcmp(argv[3], "all") == 0) {
389 strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
391 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
392 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
396 if (strcmp(argv[3], "all") == 0) {
397 strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
399 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
400 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
403 } else if (strcmp(argv[1], "kick") == 0) {
405 return RESULT_SHOWUSAGE;
406 if (strcmp(argv[3], "all") == 0) {
408 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
410 /* Kick a single user */
411 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
412 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
414 } else if(strcmp(argv[1], "list") == 0) {
415 /* List all the users in a conference */
417 ast_cli(fd, "No active conferences.\n");
418 return RESULT_SUCCESS;
421 /* Find the right conference */
423 if (strcmp(cnf->confno, argv[2]) == 0)
428 ast_cli(fd, "No such conference: %s.\n",argv[2]);
429 return RESULT_SUCCESS;
432 /* Show all the users */
433 user = cnf->firstuser;
435 ast_cli(fd, "User #: %i Channel: %s %s %s %s %s\n", user->user_no, user->chan->name, (user->userflags & CONFFLAG_ADMIN) ? "(Admin)" : "", (user->userflags & CONFFLAG_MONITOR) ? "(Listen only)" : "", (user->adminflags & ADMINFLAG_MUTED) ? "(Admn Muted)" : "", istalking(user->talking));
436 user = user->nextuser;
438 ast_cli(fd,"%d users in that conference.\n",cnf->users);
439 return RESULT_SUCCESS;
441 return RESULT_SHOWUSAGE;
442 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
443 admin_exec(NULL, cmdline);
447 static char *complete_confcmd(char *line, char *word, int pos, int state) {
448 #define CONF_COMMANDS 6
449 int which = 0, x = 0;
450 struct ast_conference *cnf = NULL;
451 struct ast_conf_user *usr = NULL;
454 char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"};
459 for (x = 0;x < CONF_COMMANDS; x++) {
460 if (!strncasecmp(cmds[x], word, strlen(word))) {
461 if (++which > state) {
462 return strdup(cmds[x]);
466 } else if (pos == 2) {
467 /* Conference Number */
468 ast_mutex_lock(&conflock);
471 if (!strncasecmp(word, cnf->confno, strlen(word))) {
477 ast_mutex_unlock(&conflock);
478 return cnf ? strdup(cnf->confno) : NULL;
479 } else if (pos == 3) {
480 /* User Number || Conf Command option*/
481 if (strstr(line, "mute") || strstr(line, "kick")) {
482 if ((state == 0) && (strstr(line, "kick") || strstr(line,"mute")) && !(strncasecmp(word, "all", strlen(word)))) {
483 return strdup("all");
486 ast_mutex_lock(&conflock);
489 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
490 myline = ast_strdupa(line);
491 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
492 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
497 if (strcmp(confno, cnf->confno) == 0) {
503 /* Search for the user */
504 usr = cnf->firstuser;
506 snprintf(usrno, sizeof(usrno), "%i", usr->user_no);
507 if (!strncasecmp(word, usrno, strlen(word))) {
514 ast_mutex_unlock(&conflock);
515 return usr ? strdup(usrno) : NULL;
521 static char conf_usage[] =
522 "Usage: meetme (un)lock|(un)mute|kick|list <confno> <usernumber>\n"
523 " Executes a command for the conference or on a conferee\n";
525 static struct ast_cli_entry cli_conf = {
526 { "meetme", NULL, NULL }, conf_cmd,
527 "Execute a command on a conference or conferee", conf_usage, complete_confcmd };
529 static int confnonzero(void *ptr)
531 struct ast_conference *conf = ptr;
533 ast_mutex_lock(&conflock);
534 res = (conf->markedusers == 0);
535 ast_mutex_unlock(&conflock);
539 static void conf_flush(int fd)
543 if (ioctl(fd, ZT_FLUSH, x)) {
544 ast_log(LOG_WARNING, "Error flushing channel\n");
549 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
551 struct ast_conference *prev=NULL, *cur;
552 struct ast_conf_user *user = malloc(sizeof(struct ast_conf_user));
553 struct ast_conf_user *usr = NULL;
555 struct zt_confinfo ztc;
557 struct ast_channel *c;
572 int using_pseudo = 0;
574 struct ast_dsp *dsp=NULL;
578 char *agifiledefault = "conf-background.agi";
579 char meetmesecs[30] = "";
580 char exitcontext[AST_MAX_EXTENSION] = "";
581 char recordingtmp[AST_MAX_EXTENSION] = "";
585 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
586 char *buf = __buf + AST_FRIENDLY_OFFSET;
589 ast_log(LOG_ERROR, "Out of memory\n");
592 memset(user, 0, sizeof(struct ast_conf_user));
594 if (confflags & CONFFLAG_RECORDCONF && conf->recording !=MEETME_RECORD_ACTIVE) {
595 conf->recordingfilename = pbx_builtin_getvar_helper(chan,"MEETME_RECORDINGFILE");
596 if (!conf->recordingfilename) {
597 snprintf(recordingtmp,sizeof(recordingtmp),"meetme-conf-rec-%s-%s",conf->confno,chan->uniqueid);
598 conf->recordingfilename = ast_strdupa(recordingtmp);
600 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
601 if (!conf->recordingformat) {
602 snprintf(recordingtmp,sizeof(recordingtmp), "wav");
603 conf->recordingformat = ast_strdupa(recordingtmp);
605 pthread_attr_init(&conf->attr);
606 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
607 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n", conf->confno, conf->recordingfilename, conf->recordingformat);
608 ast_pthread_create(&conf->recordthread, &conf->attr, recordthread, conf);
611 user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */
613 time(&user->jointime);
616 /* Sorry, but this confernce is locked! */
617 if (!ast_streamfile(chan, "conf-locked", chan->language))
618 ast_waitstream(chan, "");
623 if (confflags & CONFFLAG_MARKEDUSER)
626 ast_mutex_lock(&conflock);
627 if (conf->firstuser == NULL) {
628 /* Fill the first new User struct */
630 user->nextuser = NULL;
631 user->prevuser = NULL;
632 conf->firstuser = user;
633 conf->lastuser = user;
635 /* Fill the new user struct */
636 user->user_no = conf->lastuser->user_no + 1;
637 user->prevuser = conf->lastuser;
638 user->nextuser = NULL;
639 if (conf->lastuser->nextuser != NULL) {
640 ast_log(LOG_WARNING, "Error in User Management!\n");
641 ast_mutex_unlock(&conflock);
644 conf->lastuser->nextuser = user;
645 conf->lastuser = user;
649 user->userflags = confflags;
650 user->adminflags = 0;
652 ast_mutex_unlock(&conflock);
653 origquiet = confflags & CONFFLAG_QUIET;
654 if (confflags & CONFFLAG_EXIT_CONTEXT) {
655 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
656 strncpy(exitcontext, agifile, sizeof(exitcontext) - 1);
657 else if (!ast_strlen_zero(chan->macrocontext))
658 strncpy(exitcontext, chan->macrocontext, sizeof(exitcontext) - 1);
660 strncpy(exitcontext, chan->context, sizeof(exitcontext) - 1);
662 snprintf(user->namerecloc,sizeof(user->namerecloc),"%s/meetme-username-%s-%d",AST_SPOOL_DIR,conf->confno,user->user_no);
664 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER))
665 ast_record_review(chan,"vm-rec-name",user->namerecloc, 10,"sln", &duration, NULL);
667 while((confflags & CONFFLAG_WAITMARKED) && (conf->markedusers == 0)) {
668 confflags &= ~CONFFLAG_QUIET;
669 confflags |= origquiet;
670 /* XXX Announce that we're waiting on the conference lead to join */
671 if (!(confflags & CONFFLAG_QUIET)) {
672 res = ast_streamfile(chan, "vm-dialout", chan->language);
674 res = ast_waitstream(chan, "");
677 /* If we're waiting with hold music, set to silent mode */
679 confflags |= CONFFLAG_QUIET;
680 ast_moh_start(chan, NULL);
681 res = ast_safe_sleep_conditional(chan, 60000, confnonzero, conf);
685 ast_log(LOG_DEBUG, "Got hangup on '%s' already\n", chan->name);
690 if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
691 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
692 if (ast_waitstream(chan, "") < 0)
698 /* Set it into linear mode (write) */
699 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
700 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
704 /* Set it into linear mode (read) */
705 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
706 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
709 ast_indicate(chan, -1);
710 retryzap = strcasecmp(chan->type, "Zap");
712 origfd = chan->fds[0];
714 fd = open("/dev/zap/pseudo", O_RDWR);
716 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
720 /* Make non-blocking */
721 flags = fcntl(fd, F_GETFL);
723 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
727 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
728 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
732 /* Setup buffering information */
733 memset(&bi, 0, sizeof(bi));
734 bi.bufsize = CONF_SIZE/2;
735 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
736 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
738 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
739 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
744 if (ioctl(fd, ZT_SETLINEAR, &x)) {
745 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
751 /* XXX Make sure we're not running on a pseudo channel XXX */
755 memset(&ztc, 0, sizeof(ztc));
756 /* Check to see if we're in a conference... */
758 if (ioctl(fd, ZT_GETCONF, &ztc)) {
759 ast_log(LOG_WARNING, "Error getting conference\n");
764 /* Whoa, already in a conference... Retry... */
766 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
771 memset(&ztc, 0, sizeof(ztc));
772 /* Add us to the conference */
774 ztc.confno = conf->zapconf;
775 ast_mutex_lock(&conflock);
776 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER) && conf->users > 1) {
777 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
778 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
779 ast_waitstream(conf->chan, "");
780 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
781 ast_waitstream(conf->chan, "");
785 if (confflags & CONFFLAG_MONITOR)
786 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
787 else if (confflags & CONFFLAG_TALKER)
788 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
790 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
792 if (ioctl(fd, ZT_SETCONF, &ztc)) {
793 ast_log(LOG_WARNING, "Error setting conference\n");
795 ast_mutex_unlock(&conflock);
799 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
801 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
806 chan->name, chan->uniqueid, conf->confno, user->user_no);
808 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
810 if (!(confflags & CONFFLAG_QUIET))
811 conf_play(chan, conf, ENTER);
813 ast_mutex_unlock(&conflock);
814 if (confflags & CONFFLAG_AGI) {
816 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
817 or use default filename of conf-background.agi */
819 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
821 agifile = agifiledefault;
823 if (!strcasecmp(chan->type,"Zap")) {
824 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
826 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
828 /* Find a pointer to the agi app and execute the script */
829 app = pbx_findapp("agi");
831 ret = pbx_exec(chan, app, agifile, 1);
833 ast_log(LOG_WARNING, "Could not find application (agi)\n");
836 if (!strcasecmp(chan->type,"Zap")) {
837 /* Remove CONFMUTE mode on Zap channel */
839 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
842 if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
843 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
845 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
847 if (confflags & CONFFLAG_MONITORTALKER && !(dsp = ast_dsp_new())) {
848 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
854 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
856 /* Update the struct with the actual confflags */
857 user->userflags = confflags;
859 /* trying to add moh for single person conf */
860 if (confflags & CONFFLAG_MOH) {
861 if (conf->users == 1) {
862 if (musiconhold == 0) {
863 ast_moh_start(chan, NULL);
874 /* Leave if the last marked user left */
875 if (conf->markedusers == 0 && confflags & CONFFLAG_MARKEDEXIT) {
880 /* Check if the admin changed my modes */
881 if (user->adminflags) {
882 /* Set the new modes */
883 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
884 ztc.confmode ^= ZT_CONF_TALKER;
885 if (ioctl(fd, ZT_SETCONF, &ztc)) {
886 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
891 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
892 ztc.confmode |= ZT_CONF_TALKER;
893 if (ioctl(fd, ZT_SETCONF, &ztc)) {
894 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
899 if (user->adminflags & ADMINFLAG_KICKME) {
900 /* You have been kicked. */
901 if (!ast_streamfile(chan, "conf-kicked", chan->language))
902 ast_waitstream(chan, "");
906 } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
907 ztc.confmode |= ZT_CONF_TALKER;
908 if (ioctl(fd, ZT_SETCONF, &ztc)) {
909 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
916 if (c->fds[0] != origfd) {
918 /* Kill old pseudo */
921 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
929 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
930 if (confflags & CONFFLAG_MONITORTALKER) {
932 if (user->talking == -1)
935 res = ast_dsp_silence(dsp, f, &totalsilence);
936 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
938 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
943 chan->name, chan->uniqueid, conf->confno, user->user_no);
945 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
947 manager_event(EVENT_FLAG_CALL, "MeetmeStopTalking",
952 chan->name, chan->uniqueid, conf->confno, user->user_no);
956 /* Carefully write */
957 careful_write(fd, f->data, f->datalen);
959 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
961 tmp[0] = f->subclass;
963 if (ast_exists_extension(chan, exitcontext, tmp, 1, chan->cid.cid_num)) {
964 strncpy(chan->context, exitcontext, sizeof(chan->context) - 1);
965 strncpy(chan->exten, tmp, sizeof(chan->exten) - 1);
970 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
973 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
975 oldconfmode = ztc.confmode;
977 if (ioctl(fd, ZT_SETCONF, &ztc)) {
978 ast_log(LOG_WARNING, "Error setting conference\n");
980 ast_mutex_unlock(&conflock);
986 if ((confflags & CONFFLAG_ADMIN)) {
990 /* Record this sound! */
991 if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
992 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
999 case '1': /* Un/Mute */
1001 if (ztc.confmode & ZT_CONF_TALKER) {
1002 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1003 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1005 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1006 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1008 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1009 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1013 if (ztc.confmode & ZT_CONF_TALKER) {
1014 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1015 ast_waitstream(chan, "");
1017 if (!ast_streamfile(chan, "conf-muted", chan->language))
1018 ast_waitstream(chan, "");
1021 case '2': /* Un/Lock the Conference */
1025 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1026 ast_waitstream(chan, "");
1029 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1030 ast_waitstream(chan, "");
1033 case '3': /* Eject last user */
1035 usr = conf->lastuser;
1036 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1037 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1038 ast_waitstream(chan, "");
1040 usr->adminflags |= ADMINFLAG_KICKME;
1041 ast_stopstream(chan);
1045 /* Play an error message! */
1046 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1047 ast_waitstream(chan, "");
1055 /* Record this sound! */
1056 if (!ast_streamfile(chan, "conf-usermenu", chan->language))
1057 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1064 case '1': /* Un/Mute */
1066 if (ztc.confmode & ZT_CONF_TALKER) {
1067 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1068 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1069 } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
1070 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1071 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1073 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1074 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1078 if (ztc.confmode & ZT_CONF_TALKER) {
1079 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1080 ast_waitstream(chan, "");
1082 if (!ast_streamfile(chan, "conf-muted", chan->language))
1083 ast_waitstream(chan, "");
1088 /* Play an error message! */
1089 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1090 ast_waitstream(chan, "");
1096 ast_moh_start(chan, NULL);
1098 ztc.confmode = oldconfmode;
1099 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1100 ast_log(LOG_WARNING, "Error setting conference\n");
1102 ast_mutex_unlock(&conflock);
1106 } else if (option_debug) {
1107 ast_log(LOG_DEBUG, "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",chan->name,f->frametype,f->subclass);
1110 } else if (outfd > -1) {
1111 res = read(outfd, buf, CONF_SIZE);
1113 memset(&fr, 0, sizeof(fr));
1114 fr.frametype = AST_FRAME_VOICE;
1115 fr.subclass = AST_FORMAT_SLINEAR;
1119 fr.offset = AST_FRIENDLY_OFFSET;
1120 if (ast_write(chan, &fr) < 0) {
1121 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1125 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1132 /* Take out of conference */
1133 /* Add us to the conference */
1137 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1138 ast_log(LOG_WARNING, "Error setting conference\n");
1142 ast_mutex_lock(&conflock);
1143 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1144 conf_play(chan, conf, LEAVE);
1146 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
1147 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
1148 if ((conf->chan) && (conf->users > 1)) {
1149 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1150 ast_waitstream(conf->chan, "");
1151 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1152 ast_waitstream(conf->chan, "");
1154 ast_filedelete(user->namerecloc, NULL);
1157 ast_mutex_unlock(&conflock);
1161 ast_mutex_lock(&conflock);
1162 if (confflags & CONFFLAG_MONITORTALKER && dsp)
1165 if (user->user_no) { /* Only cleanup users who really joined! */
1166 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
1171 chan->name, chan->uniqueid, conf->confno, user->user_no);
1174 if (confflags & CONFFLAG_MARKEDUSER)
1175 conf->markedusers--;
1178 /* No more users -- close this one out */
1182 prev->next = conf->next;
1191 ast_log(LOG_WARNING, "Conference not found\n");
1192 if (conf->recording == MEETME_RECORD_ACTIVE) {
1193 conf->recording = MEETME_RECORD_TERMINATE;
1194 ast_mutex_unlock(&conflock);
1196 ast_mutex_lock(&conflock);
1197 if (conf->recording == MEETME_RECORD_OFF)
1199 ast_mutex_unlock(&conflock);
1203 ast_hangup(conf->chan);
1208 /* Remove the user struct */
1209 if (user == conf->firstuser) {
1210 if (user->nextuser) {
1211 /* There is another entry */
1212 user->nextuser->prevuser = NULL;
1214 /* We are the only entry */
1215 conf->lastuser = NULL;
1217 /* In either case */
1218 conf->firstuser = user->nextuser;
1219 } else if (user == conf->lastuser){
1221 user->prevuser->nextuser = NULL;
1223 ast_log(LOG_ERROR, "Bad bad bad! We're the last, not the first, but nobody before us??\n");
1224 conf->lastuser = user->prevuser;
1227 user->nextuser->prevuser = user->prevuser;
1229 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
1231 user->prevuser->nextuser = user->nextuser;
1233 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
1236 /* Return the number of seconds the user was in the conf */
1237 snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (time(NULL) - user->jointime));
1238 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1241 ast_mutex_unlock(&conflock);
1245 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
1247 struct ast_config *cfg;
1248 struct ast_variable *var;
1249 struct ast_conference *cnf;
1251 /* Check first in the conference list */
1252 ast_mutex_lock(&conflock);
1255 if (!strcmp(confno, cnf->confno))
1259 ast_mutex_unlock(&conflock);
1263 /* No need to parse meetme.conf */
1264 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1266 if (dynamic_pin[0] == 'q') {
1267 /* Query the user to enter a PIN */
1268 ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
1270 cnf = build_conf(confno, dynamic_pin, "", make, dynamic);
1272 cnf = build_conf(confno, "", "", make, dynamic);
1275 /* Check the config */
1276 cfg = ast_config_load("meetme.conf");
1278 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
1281 var = ast_variable_browse(cfg, "rooms");
1283 if (!strcasecmp(var->name, "conf")) {
1284 /* Separate the PIN */
1285 char *pin, *pinadmin, *conf;
1287 if ((pinadmin = ast_strdupa(var->value))) {
1288 conf = strsep(&pinadmin, "|,");
1289 pin = strsep(&pinadmin, "|,");
1290 if (!strcasecmp(conf, confno)) {
1291 /* Bingo it's a valid conference */
1294 cnf = build_conf(confno, pin, pinadmin, make, dynamic);
1296 cnf = build_conf(confno, pin, "", make, dynamic);
1299 cnf = build_conf(confno, "", pinadmin, make, dynamic);
1301 cnf = build_conf(confno, "", "", make, dynamic);
1309 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1311 ast_config_destroy(cfg);
1313 } else if (dynamic_pin) {
1314 /* Correct for the user selecting 'D' instead of 'd' to have
1315 someone join into a conference that has already been created
1317 if (dynamic_pin[0] == 'q')
1318 dynamic_pin[0] = '\0';
1323 /*--- count_exec: The MeetmeCount application */
1324 static int count_exec(struct ast_channel *chan, void *data)
1326 struct localuser *u;
1328 struct ast_conference *conf;
1330 char *confnum, *localdata;
1333 if (!data || ast_strlen_zero(data)) {
1334 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1337 localdata = ast_strdupa(data);
1339 confnum = strsep(&localdata,"|");
1340 conf = find_conf(chan, confnum, 0, 0, NULL);
1342 count = conf->users;
1346 if (localdata && !ast_strlen_zero(localdata)){
1347 /* have var so load it and exit */
1348 snprintf(val,sizeof(val), "%i",count);
1349 pbx_builtin_setvar_helper(chan, localdata,val);
1351 if (chan->_state != AST_STATE_UP)
1353 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1355 LOCAL_USER_REMOVE(u);
1359 /*--- conf_exec: The meetme() application */
1360 static int conf_exec(struct ast_channel *chan, void *data)
1363 struct localuser *u;
1364 char confno[AST_MAX_EXTENSION] = "";
1367 struct ast_conference *cnf;
1370 int empty = 0, empty_no_pin = 0;
1371 int always_prompt = 0;
1372 char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
1374 if (!data || ast_strlen_zero(data)) {
1381 if (chan->_state != AST_STATE_UP)
1384 info = ast_strdupa((char *)notdata);
1387 char *tmp = strsep(&info, "|");
1388 strncpy(confno, tmp, sizeof(confno) - 1);
1389 if (ast_strlen_zero(confno)) {
1394 inflags = strsep(&info, "|");
1396 inpin = strsep(&info, "|");
1398 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
1401 if (strchr(inflags, 'a'))
1402 confflags |= CONFFLAG_ADMIN;
1403 if (strchr(inflags, 'T'))
1404 confflags |= CONFFLAG_MONITORTALKER;
1405 if (strchr(inflags, 'i'))
1406 confflags |= CONFFLAG_INTROUSER;
1407 if (strchr(inflags, 'm'))
1408 confflags |= CONFFLAG_MONITOR;
1409 if (strchr(inflags, 'p'))
1410 confflags |= CONFFLAG_POUNDEXIT;
1411 if (strchr(inflags, 's'))
1412 confflags |= CONFFLAG_STARMENU;
1413 if (strchr(inflags, 't'))
1414 confflags |= CONFFLAG_TALKER;
1415 if (strchr(inflags, 'q'))
1416 confflags |= CONFFLAG_QUIET;
1417 if (strchr(inflags, 'M'))
1418 confflags |= CONFFLAG_MOH;
1419 if (strchr(inflags, 'x'))
1420 confflags |= CONFFLAG_MARKEDEXIT;
1421 if (strchr(inflags, 'X'))
1422 confflags |= CONFFLAG_EXIT_CONTEXT;
1423 if (strchr(inflags, 'A'))
1424 confflags |= CONFFLAG_MARKEDUSER;
1425 if (strchr(inflags, 'b'))
1426 confflags |= CONFFLAG_AGI;
1427 if (strchr(inflags, 'w'))
1428 confflags |= CONFFLAG_WAITMARKED;
1429 if (strchr(inflags, 'r'))
1430 confflags |= CONFFLAG_RECORDCONF;
1431 if (strchr(inflags, 'd'))
1433 if (strchr(inflags, 'D')) {
1436 strncpy(the_pin, "q", sizeof(the_pin) - 1);
1439 if (strchr(inflags, 'e'))
1441 if (strchr(inflags, 'E')) {
1445 if (strchr(inflags, 'P'))
1454 struct ast_config *cfg;
1455 struct ast_variable *var;
1458 memset(map, 0, sizeof(map));
1460 ast_mutex_lock(&conflock);
1463 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
1464 /* Disqualify in use conference */
1465 if (confno_int >= 0 && confno_int < 1024)
1470 ast_mutex_unlock(&conflock);
1472 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
1473 if ((empty_no_pin) || (!dynamic)) {
1474 cfg = ast_config_load("meetme.conf");
1476 var = ast_variable_browse(cfg, "rooms");
1478 if (!strcasecmp(var->name, "conf")) {
1479 char *stringp = ast_strdupa(var->value);
1481 char *confno_tmp = strsep(&stringp, "|,");
1483 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
1484 if ((confno_int >= 0) && (confno_int < 1024)) {
1485 if (stringp && empty_no_pin) {
1491 /* For static: run through the list and see if this conference is empty */
1492 ast_mutex_lock(&conflock);
1495 if (!strcmp(confno_tmp, cnf->confno)) {
1496 /* The conference exists, therefore it's not empty */
1502 ast_mutex_unlock(&conflock);
1504 /* At this point, we have a confno_tmp (static conference) that is empty */
1505 if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) {
1506 /* Case 1: empty_no_pin and pin is nonexistant (NULL)
1507 * Case 2: empty_no_pin and pin is blank (but not NULL)
1508 * Case 3: not empty_no_pin
1510 strncpy(confno, confno_tmp, sizeof(confno) - 1);
1512 /* XXX the map is not complete (but we do have a confno) */
1517 ast_log(LOG_ERROR, "Out of memory\n");
1522 ast_config_destroy(cfg);
1525 /* Select first conference number not in use */
1526 if (ast_strlen_zero(confno) && dynamic) {
1527 for (i=0;i<1024;i++) {
1529 snprintf(confno, sizeof(confno), "%d", i);
1536 if (ast_strlen_zero(confno)) {
1537 res = ast_streamfile(chan, "conf-noempty", chan->language);
1539 ast_waitstream(chan, "");
1541 if (sscanf(confno, "%d", &confno_int) == 1) {
1542 res = ast_streamfile(chan, "conf-enteringno", chan->language);
1544 ast_waitstream(chan, "");
1545 res = ast_say_digits(chan, confno_int, "", chan->language);
1548 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
1552 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
1553 /* Prompt user for conference number */
1554 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
1556 /* Don't try to validate when we catch an error */
1562 if (!ast_strlen_zero(confno)) {
1563 /* Check the validity of the conference */
1564 cnf = find_conf(chan, confno, 1, dynamic, the_pin);
1566 res = ast_streamfile(chan, "conf-invalid", chan->language);
1568 ast_waitstream(chan, "");
1573 if ((!ast_strlen_zero(cnf->pin) && ! (confflags & CONFFLAG_ADMIN)) || (!ast_strlen_zero(cnf->pinadmin) && (confflags & CONFFLAG_ADMIN))) {
1574 char pin[AST_MAX_EXTENSION]="";
1577 /* Allow the pin to be retried up to 3 times */
1578 for (j=0; j<3; j++) {
1579 if (*the_pin && (always_prompt==0)) {
1580 strncpy(pin, the_pin, sizeof(pin) - 1);
1583 /* Prompt user for pin if pin is required */
1584 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
1587 if (!strcasecmp(pin, cnf->pin) || (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))) {
1591 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
1592 confflags |= CONFFLAG_ADMIN;
1593 /* Run the conference */
1594 res = conf_run(chan, cnf, confflags);
1598 res = ast_streamfile(chan, "conf-invalidpin", chan->language);
1600 ast_waitstream(chan, AST_DIGIT_ANY);
1615 /* Don't retry pin with a static pin */
1616 if (*the_pin && (always_prompt==0)) {
1621 /* No pin required */
1624 /* Run the conference */
1625 res = conf_run(chan, cnf, confflags);
1629 } while (allowretry);
1630 /* Do the conference */
1631 LOCAL_USER_REMOVE(u);
1635 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
1636 struct ast_conf_user *user = NULL;
1637 char usrno[1024] = "";
1638 if (conf && callerident) {
1639 user = conf->firstuser;
1641 snprintf(usrno, sizeof(usrno), "%i", user->user_no);
1642 if (strcmp(usrno, callerident) == 0)
1644 user = user->nextuser;
1650 /*--- admin_exec: The MeetMeadmin application */
1651 /* MeetMeAdmin(confno, command, caller) */
1652 static int admin_exec(struct ast_channel *chan, void *data) {
1653 char *params, *command = NULL, *caller = NULL, *conf = NULL;
1654 struct ast_conference *cnf;
1655 struct ast_conf_user *user = NULL;
1657 ast_mutex_lock(&conflock);
1658 /* The param has the conference number the user and the command to execute */
1659 if (data && !ast_strlen_zero(data)) {
1660 params = ast_strdupa((char *) data);
1661 conf = strsep(¶ms, "|");
1662 command = strsep(¶ms, "|");
1663 caller = strsep(¶ms, "|");
1666 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
1667 ast_mutex_unlock(&conflock);
1672 if (strcmp(cnf->confno, conf) == 0)
1678 user = find_user(cnf, caller);
1681 switch((int) (*command)) {
1682 case 76: /* L: Lock */
1685 case 108: /* l: Unlock */
1688 case 75: /* K: kick all users*/
1689 user = cnf->firstuser;
1691 user->adminflags |= ADMINFLAG_KICKME;
1692 if (user->nextuser) {
1693 user = user->nextuser;
1699 case 101: /* e: Eject last user*/
1700 user = cnf->lastuser;
1701 if (!(user->userflags & CONFFLAG_ADMIN)) {
1702 user->adminflags |= ADMINFLAG_KICKME;
1705 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
1707 case 77: /* M: Mute */
1709 user->adminflags |= ADMINFLAG_MUTED;
1711 ast_log(LOG_NOTICE, "Specified User not found!\n");
1714 case 78: /* N: Mute all users */
1715 user = cnf->firstuser;
1717 if (user && !(user->userflags & CONFFLAG_ADMIN))
1718 user->adminflags |= ADMINFLAG_MUTED;
1719 if (user->nextuser) {
1720 user = user->nextuser;
1726 case 109: /* m: Unmute */
1727 if (user && (user->adminflags & ADMINFLAG_MUTED)) {
1728 user->adminflags ^= ADMINFLAG_MUTED;
1730 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
1733 case 110: /* n: Unmute all users */
1734 user = cnf->firstuser;
1736 if (user && (user-> adminflags & ADMINFLAG_MUTED)) {
1737 user->adminflags ^= ADMINFLAG_MUTED;
1739 if (user->nextuser) {
1740 user = user->nextuser;
1746 case 107: /* k: Kick user */
1748 user->adminflags |= ADMINFLAG_KICKME;
1750 ast_log(LOG_NOTICE, "Specified User not found!");
1755 ast_log(LOG_NOTICE, "Conference Number not found\n");
1758 ast_mutex_unlock(&conflock);
1762 static void *recordthread(void *args)
1764 struct ast_conference *cnf;
1765 struct ast_frame *f=NULL;
1767 struct ast_filestream *s;
1770 cnf = (struct ast_conference *)args;
1771 if( !cnf || !cnf->chan ) {
1774 ast_stopstream(cnf->chan);
1775 flags = O_CREAT|O_TRUNC|O_WRONLY;
1776 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
1779 cnf->recording = MEETME_RECORD_ACTIVE;
1780 while (ast_waitfor(cnf->chan, -1) > -1) {
1781 f = ast_read(cnf->chan);
1786 if (f->frametype == AST_FRAME_VOICE) {
1787 res = ast_writestream(s, f);
1792 if (cnf->recording == MEETME_RECORD_TERMINATE) {
1793 ast_mutex_lock(&conflock);
1794 ast_mutex_unlock(&conflock);
1798 cnf->recording = MEETME_RECORD_OFF;
1804 int unload_module(void)
1806 STANDARD_HANGUP_LOCALUSERS;
1807 ast_cli_unregister(&cli_show_confs);
1808 ast_cli_unregister(&cli_conf);
1809 ast_unregister_application(app3);
1810 ast_unregister_application(app2);
1811 return ast_unregister_application(app);
1814 int load_module(void)
1816 ast_cli_register(&cli_show_confs);
1817 ast_cli_register(&cli_conf);
1818 ast_register_application(app3, admin_exec, synopsis3, descrip3);
1819 ast_register_application(app2, count_exec, synopsis2, descrip2);
1820 return ast_register_application(app, conf_exec, synopsis, descrip);
1824 char *description(void)
1832 STANDARD_USECOUNT(res);
1838 return ASTERISK_GPL_KEY;