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 struct ast_conference *next;
134 struct ast_conf_user {
135 int user_no; /* User Number */
136 struct ast_conf_user *prevuser; /* Pointer to the previous user */
137 struct ast_conf_user *nextuser; /* Pointer to the next user */
138 int userflags; /* Flags as set in the conference */
139 int adminflags; /* Flags set by the Admin */
140 struct ast_channel *chan; /* Connected channel */
141 int talking; /* Is user talking */
142 char usrvalue[50]; /* Custom User Value */
143 char namerecloc[AST_MAX_EXTENSION]; /* Name Recorded file Location */
144 time_t jointime; /* Time the user joined the conference */
147 #define ADMINFLAG_MUTED (1 << 1) /* User is muted */
148 #define ADMINFLAG_KICKME (1 << 2) /* User is kicked */
149 #define MEETME_DELAYDETECTTALK 300
150 #define MEETME_DELAYDETECTENDTALK 1000
152 AST_MUTEX_DEFINE_STATIC(conflock);
154 static int admin_exec(struct ast_channel *chan, void *data);
156 static void *recordthread(void *args);
164 #define MEETME_RECORD_OFF 0
165 #define MEETME_RECORD_ACTIVE 1
166 #define MEETME_RECORD_TERMINATE 2
168 #define CONF_SIZE 320
170 #define CONFFLAG_ADMIN (1 << 1) /* If set the user has admin access on the conference */
171 #define CONFFLAG_MONITOR (1 << 2) /* If set the user can only receive audio from the conference */
172 #define CONFFLAG_POUNDEXIT (1 << 3) /* If set asterisk will exit conference when '#' is pressed */
173 #define CONFFLAG_STARMENU (1 << 4) /* If set asterisk will provide a menu to the user what '*' is pressed */
174 #define CONFFLAG_TALKER (1 << 5) /* If set the use can only send audio to the conference */
175 #define CONFFLAG_QUIET (1 << 6) /* If set there will be no enter or leave sounds */
176 #define CONFFLAG_VIDEO (1 << 7) /* Set to enable video mode */
177 #define CONFFLAG_AGI (1 << 8) /* Set to run AGI Script in Background */
178 #define CONFFLAG_MOH (1 << 9) /* Set to have music on hold when user is alone in conference */
179 #define CONFFLAG_MARKEDEXIT (1 << 10) /* If set the MeetMe will return if all marked with this flag left */
180 #define CONFFLAG_WAITMARKED (1 << 11) /* If set, the MeetMe will wait until a marked user enters */
181 #define CONFFLAG_EXIT_CONTEXT (1 << 12) /* If set, the MeetMe will exit to the specified context */
182 #define CONFFLAG_MARKEDUSER (1 << 13) /* If set, the user will be marked */
183 #define CONFFLAG_INTROUSER (1 << 14) /* If set, user will be ask record name on entry of conference */
184 #define CONFFLAG_RECORDCONF (1<< 15) /* If set, the MeetMe will be recorded */
185 #define CONFFLAG_MONITORTALKER (1 << 16) /* If set, the user will be monitored if the user is talking or not */
187 static char *istalking(int x)
192 return "(unmonitored)";
194 return "(not talking)";
197 static int careful_write(int fd, unsigned char *data, int len)
202 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
203 res = ioctl(fd, ZT_IOMUX, &x);
205 res = write(fd, data, len);
207 if (errno != EAGAIN) {
208 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
219 static void conf_play(struct ast_conference *conf, int sound)
223 ast_mutex_lock(&conflock);
238 careful_write(conf->fd, data, len);
239 ast_mutex_unlock(&conflock);
242 static struct ast_conference *build_conf(char *confno, char *pin, int make, int dynamic)
244 struct ast_conference *cnf;
245 struct zt_confinfo ztc;
246 ast_mutex_lock(&conflock);
249 if (!strcmp(confno, cnf->confno))
253 if (!cnf && (make || dynamic)) {
254 cnf = malloc(sizeof(struct ast_conference));
257 memset(cnf, 0, sizeof(struct ast_conference));
258 strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1);
259 strncpy(cnf->pin, pin, sizeof(cnf->pin) - 1);
260 cnf->markedusers = 0;
261 cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo", NULL);
263 cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */
265 ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
266 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
268 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
274 memset(&ztc, 0, sizeof(ztc));
275 /* Setup a new zap conference */
278 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
279 if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
280 ast_log(LOG_WARNING, "Error setting conference\n");
282 ast_hangup(cnf->chan);
289 /* Fill the conference struct */
290 cnf->start = time(NULL);
291 cnf->zapconf = ztc.confno;
292 cnf->isdynamic = dynamic;
293 cnf->firstuser = NULL;
294 cnf->lastuser = NULL;
296 if (option_verbose > 2)
297 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
301 ast_log(LOG_WARNING, "Out of memory\n");
304 ast_mutex_unlock(&conflock);
308 static int confs_show(int fd, int argc, char **argv)
310 ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
311 return RESULT_SUCCESS;
314 static char show_confs_usage[] =
315 "Deprecated! Please use 'meetme' instead.\n";
317 static struct ast_cli_entry cli_show_confs = {
318 { "show", "conferences", NULL }, confs_show,
319 "Show status of conferences", show_confs_usage, NULL };
321 static int conf_cmd(int fd, int argc, char **argv) {
322 /* Process the command */
323 struct ast_conference *cnf;
324 struct ast_conf_user *user;
326 int i = 0, total = 0;
328 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
329 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
330 char cmdline[1024] = "";
333 ast_cli(fd, "Invalid Arguments.\n");
334 /* Check for length so no buffer will overflow... */
335 for (i = 0; i < argc; i++) {
336 if (strlen(argv[i]) > 100)
337 ast_cli(fd, "Invalid Arguments.\n");
340 /* 'MeetMe': List all the conferences */
344 ast_cli(fd, "No active MeetMe conferences.\n");
345 return RESULT_SUCCESS;
347 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
349 if (cnf->markedusers == 0)
350 strncpy(cmdline, "N/A ", sizeof(cmdline) - 1);
352 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
353 hr = (now - cnf->start) / 3600;
354 min = ((now - cnf->start) % 3600) / 60;
355 sec = (now - cnf->start) % 60;
357 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
362 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
363 return RESULT_SUCCESS;
366 return RESULT_SHOWUSAGE;
367 strncpy(cmdline, argv[2], sizeof(cmdline) - 1); /* Argv 2: conference number */
368 if (strstr(argv[1], "lock")) {
369 if (strcmp(argv[1], "lock") == 0) {
371 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
374 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
376 } else if (strstr(argv[1], "mute")) {
378 return RESULT_SHOWUSAGE;
379 if (strcmp(argv[1], "mute") == 0) {
381 if (strcmp(argv[3], "all") == 0) {
382 strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
384 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
385 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
389 if (strcmp(argv[3], "all") == 0) {
390 strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
392 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
393 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
396 } else if (strcmp(argv[1], "kick") == 0) {
398 return RESULT_SHOWUSAGE;
399 if (strcmp(argv[3], "all") == 0) {
401 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
403 /* Kick a single user */
404 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
405 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
407 } else if(strcmp(argv[1], "list") == 0) {
408 /* List all the users in a conference */
410 ast_cli(fd, "No active conferences.\n");
411 return RESULT_SUCCESS;
414 /* Find the right conference */
416 if (strcmp(cnf->confno, argv[2]) == 0)
421 ast_cli(fd, "No such conference: %s.\n",argv[2]);
422 return RESULT_SUCCESS;
425 /* Show all the users */
426 user = cnf->firstuser;
428 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));
429 user = user->nextuser;
431 return RESULT_SUCCESS;
433 return RESULT_SHOWUSAGE;
434 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
435 admin_exec(NULL, cmdline);
439 static char *complete_confcmd(char *line, char *word, int pos, int state) {
440 #define CONF_COMMANDS 6
441 int which = 0, x = 0;
442 struct ast_conference *cnf = NULL;
443 struct ast_conf_user *usr = NULL;
446 char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"};
451 for (x = 0;x < CONF_COMMANDS; x++) {
452 if (!strncasecmp(cmds[x], word, strlen(word))) {
453 if (++which > state) {
454 return strdup(cmds[x]);
458 } else if (pos == 2) {
459 /* Conference Number */
460 ast_mutex_lock(&conflock);
463 if (!strncasecmp(word, cnf->confno, strlen(word))) {
469 ast_mutex_unlock(&conflock);
470 return cnf ? strdup(cnf->confno) : NULL;
471 } else if (pos == 3) {
472 /* User Number || Conf Command option*/
473 if (strstr(line, "mute") || strstr(line, "kick")) {
474 if ((state == 0) && (strstr(line, "kick") || strstr(line,"mute")) && !(strncasecmp(word, "all", strlen(word)))) {
475 return strdup("all");
478 ast_mutex_lock(&conflock);
481 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
482 myline = ast_strdupa(line);
483 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
484 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
489 if (strcmp(confno, cnf->confno) == 0) {
495 /* Search for the user */
496 usr = cnf->firstuser;
498 snprintf(usrno, sizeof(usrno), "%i", usr->user_no);
499 if (!strncasecmp(word, usrno, strlen(word))) {
506 ast_mutex_unlock(&conflock);
507 return usr ? strdup(usrno) : NULL;
513 static char conf_usage[] =
514 "Usage: meetme (un)lock|(un)mute|kick|list <confno> <usernumber>\n"
515 " Executes a command for the conference or on a conferee\n";
517 static struct ast_cli_entry cli_conf = {
518 { "meetme", NULL, NULL }, conf_cmd,
519 "Execute a command on a conference or conferee", conf_usage, complete_confcmd };
521 static int confnonzero(void *ptr)
523 struct ast_conference *conf = ptr;
525 ast_mutex_lock(&conflock);
526 res = (conf->markedusers == 0);
527 ast_mutex_unlock(&conflock);
531 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
533 struct ast_conference *prev=NULL, *cur;
534 struct ast_conf_user *user = malloc(sizeof(struct ast_conf_user));
535 struct ast_conf_user *usr = NULL;
537 struct zt_confinfo ztc;
539 struct ast_channel *c;
554 int using_pseudo = 0;
556 struct ast_dsp *dsp=NULL;
560 char *agifiledefault = "conf-background.agi";
561 char meetmesecs[30] = "";
562 char exitcontext[AST_MAX_EXTENSION] = "";
563 char recordingtmp[AST_MAX_EXTENSION] = "";
567 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
568 char *buf = __buf + AST_FRIENDLY_OFFSET;
571 ast_log(LOG_ERROR, "Out of memory\n");
574 memset(user, 0, sizeof(struct ast_conf_user));
576 if (confflags & CONFFLAG_RECORDCONF && conf->recording !=MEETME_RECORD_ACTIVE) {
577 conf->recordingfilename = pbx_builtin_getvar_helper(chan,"MEETME_RECORDINGFILE");
578 if (!conf->recordingfilename) {
579 snprintf(recordingtmp,sizeof(recordingtmp),"meetme-conf-rec-%s-%s",conf->confno,chan->uniqueid);
580 conf->recordingfilename = ast_strdupa(recordingtmp);
582 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
583 if (!conf->recordingformat) {
584 snprintf(recordingtmp,sizeof(recordingtmp), "wav");
585 conf->recordingformat = ast_strdupa(recordingtmp);
587 pthread_attr_init(&conf->attr);
588 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
589 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n", conf->confno, conf->recordingfilename, conf->recordingformat);
590 ast_pthread_create(&conf->recordthread, &conf->attr, recordthread, conf);
593 user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */
595 time(&user->jointime);
598 /* Sorry, but this confernce is locked! */
599 if (!ast_streamfile(chan, "conf-locked", chan->language))
600 ast_waitstream(chan, "");
605 if (confflags & CONFFLAG_MARKEDUSER)
608 ast_mutex_lock(&conflock);
609 if (conf->firstuser == NULL) {
610 /* Fill the first new User struct */
612 user->nextuser = NULL;
613 user->prevuser = NULL;
614 conf->firstuser = user;
615 conf->lastuser = user;
617 /* Fill the new user struct */
618 user->user_no = conf->lastuser->user_no + 1;
619 user->prevuser = conf->lastuser;
620 user->nextuser = NULL;
621 if (conf->lastuser->nextuser != NULL) {
622 ast_log(LOG_WARNING, "Error in User Management!\n");
623 ast_mutex_unlock(&conflock);
626 conf->lastuser->nextuser = user;
627 conf->lastuser = user;
631 user->userflags = confflags;
632 user->adminflags = 0;
634 ast_mutex_unlock(&conflock);
635 origquiet = confflags & CONFFLAG_QUIET;
636 if (confflags & CONFFLAG_EXIT_CONTEXT) {
637 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
638 strncpy(exitcontext, agifile, sizeof(exitcontext) - 1);
639 else if (!ast_strlen_zero(chan->macrocontext))
640 strncpy(exitcontext, chan->macrocontext, sizeof(exitcontext) - 1);
642 strncpy(exitcontext, chan->context, sizeof(exitcontext) - 1);
644 snprintf(user->namerecloc,sizeof(user->namerecloc),"%s/meetme-username-%s-%d",AST_SPOOL_DIR,conf->confno,user->user_no);
646 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER))
647 ast_record_review(chan,"vm-rec-name",user->namerecloc, 10,"sln", &duration, NULL);
649 while((confflags & CONFFLAG_WAITMARKED) && (conf->markedusers == 0)) {
650 confflags &= ~CONFFLAG_QUIET;
651 confflags |= origquiet;
652 /* XXX Announce that we're waiting on the conference lead to join */
653 if (!(confflags & CONFFLAG_QUIET)) {
654 res = ast_streamfile(chan, "vm-dialout", chan->language);
656 res = ast_waitstream(chan, "");
659 /* If we're waiting with hold music, set to silent mode */
661 confflags |= CONFFLAG_QUIET;
662 ast_moh_start(chan, NULL);
663 res = ast_safe_sleep_conditional(chan, 60000, confnonzero, conf);
667 ast_log(LOG_DEBUG, "Got hangup on '%s' already\n", chan->name);
672 if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
673 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
674 if (ast_waitstream(chan, "") < 0)
680 /* Set it into linear mode (write) */
681 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
682 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
686 /* Set it into linear mode (read) */
687 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
688 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
691 ast_indicate(chan, -1);
692 retryzap = strcasecmp(chan->type, "Zap");
694 origfd = chan->fds[0];
696 fd = open("/dev/zap/pseudo", O_RDWR);
698 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
702 /* Make non-blocking */
703 flags = fcntl(fd, F_GETFL);
705 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
709 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
710 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
714 /* Setup buffering information */
715 memset(&bi, 0, sizeof(bi));
716 bi.bufsize = CONF_SIZE/2;
717 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
718 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
720 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
721 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
726 if (ioctl(fd, ZT_SETLINEAR, &x)) {
727 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
733 /* XXX Make sure we're not running on a pseudo channel XXX */
737 memset(&ztc, 0, sizeof(ztc));
738 /* Check to see if we're in a conference... */
740 if (ioctl(fd, ZT_GETCONF, &ztc)) {
741 ast_log(LOG_WARNING, "Error getting conference\n");
746 /* Whoa, already in a conference... Retry... */
748 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
753 memset(&ztc, 0, sizeof(ztc));
754 /* Add us to the conference */
756 ztc.confno = conf->zapconf;
757 ast_mutex_lock(&conflock);
758 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER) && conf->users > 1) {
759 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
760 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
761 ast_waitstream(conf->chan, "");
762 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
763 ast_waitstream(conf->chan, "");
767 if (confflags & CONFFLAG_MONITOR)
768 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
769 else if (confflags & CONFFLAG_TALKER)
770 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
772 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
774 if (ioctl(fd, ZT_SETCONF, &ztc)) {
775 ast_log(LOG_WARNING, "Error setting conference\n");
777 ast_mutex_unlock(&conflock);
780 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
782 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
787 chan->name, chan->uniqueid, conf->confno, user->user_no);
789 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
791 if (!(confflags & CONFFLAG_QUIET))
792 conf_play(conf, ENTER);
794 ast_mutex_unlock(&conflock);
795 if (confflags & CONFFLAG_AGI) {
797 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
798 or use default filename of conf-background.agi */
800 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
802 agifile = agifiledefault;
804 if (!strcasecmp(chan->type,"Zap")) {
805 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
807 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
809 /* Find a pointer to the agi app and execute the script */
810 app = pbx_findapp("agi");
812 ret = pbx_exec(chan, app, agifile, 1);
814 ast_log(LOG_WARNING, "Could not find application (agi)\n");
817 if (!strcasecmp(chan->type,"Zap")) {
818 /* Remove CONFMUTE mode on Zap channel */
820 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
823 if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
824 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
826 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
828 if (confflags & CONFFLAG_MONITORTALKER && !(dsp = ast_dsp_new())) {
829 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
835 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
837 /* Update the struct with the actual confflags */
838 user->userflags = confflags;
840 /* trying to add moh for single person conf */
841 if (confflags & CONFFLAG_MOH) {
842 if (conf->users == 1) {
843 if (musiconhold == 0) {
844 ast_moh_start(chan, NULL);
855 /* Leave if the last marked user left */
856 if (conf->markedusers == 0 && confflags & CONFFLAG_MARKEDEXIT) {
861 /* Check if the admin changed my modes */
862 if (user->adminflags) {
863 /* Set the new modes */
864 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
865 ztc.confmode ^= ZT_CONF_TALKER;
866 if (ioctl(fd, ZT_SETCONF, &ztc)) {
867 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
872 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
873 ztc.confmode |= ZT_CONF_TALKER;
874 if (ioctl(fd, ZT_SETCONF, &ztc)) {
875 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
880 if (user->adminflags & ADMINFLAG_KICKME) {
881 /* You have been kicked. */
882 if (!ast_streamfile(chan, "conf-kicked", chan->language))
883 ast_waitstream(chan, "");
887 } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
888 ztc.confmode |= ZT_CONF_TALKER;
889 if (ioctl(fd, ZT_SETCONF, &ztc)) {
890 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
897 if (c->fds[0] != origfd) {
899 /* Kill old pseudo */
902 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
910 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
911 if (confflags & CONFFLAG_MONITORTALKER) {
913 if (user->talking == -1)
916 res = ast_dsp_silence(dsp, f, &totalsilence);
917 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
919 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
924 chan->name, chan->uniqueid, conf->confno, user->user_no);
926 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
928 manager_event(EVENT_FLAG_CALL, "MeetmeStopTalking",
933 chan->name, chan->uniqueid, conf->confno, user->user_no);
937 /* Carefully write */
938 careful_write(fd, f->data, f->datalen);
940 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
942 tmp[0] = f->subclass;
944 if (ast_exists_extension(chan, exitcontext, tmp, 1, chan->cid.cid_num)) {
945 strncpy(chan->context, exitcontext, sizeof(chan->context) - 1);
946 strncpy(chan->exten, tmp, sizeof(chan->exten) - 1);
951 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
954 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
958 if ((confflags & CONFFLAG_ADMIN)) {
962 /* Record this sound! */
963 if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
964 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
971 case '1': /* Un/Mute */
973 if (ztc.confmode & ZT_CONF_TALKER) {
974 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
975 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
977 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
978 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
980 if (ioctl(fd, ZT_SETCONF, &ztc)) {
981 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
985 if (ztc.confmode & ZT_CONF_TALKER) {
986 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
987 ast_waitstream(chan, "");
989 if (!ast_streamfile(chan, "conf-muted", chan->language))
990 ast_waitstream(chan, "");
993 case '2': /* Un/Lock the Conference */
997 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
998 ast_waitstream(chan, "");
1001 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1002 ast_waitstream(chan, "");
1005 case '3': /* Eject last user */
1007 usr = conf->lastuser;
1008 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1009 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1010 ast_waitstream(chan, "");
1012 usr->adminflags |= ADMINFLAG_KICKME;
1013 ast_stopstream(chan);
1017 /* Play an error message! */
1018 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1019 ast_waitstream(chan, "");
1027 /* Record this sound! */
1028 if (!ast_streamfile(chan, "conf-usermenu", chan->language))
1029 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1036 case '1': /* Un/Mute */
1038 if (ztc.confmode & ZT_CONF_TALKER) {
1039 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1040 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1041 } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
1042 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1043 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1045 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1046 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1050 if (ztc.confmode & ZT_CONF_TALKER) {
1051 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1052 ast_waitstream(chan, "");
1054 if (!ast_streamfile(chan, "conf-muted", chan->language))
1055 ast_waitstream(chan, "");
1060 /* Play an error message! */
1061 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1062 ast_waitstream(chan, "");
1068 ast_moh_start(chan, NULL);
1070 } else if (option_debug) {
1071 ast_log(LOG_DEBUG, "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",chan->name,f->frametype,f->subclass);
1074 } else if (outfd > -1) {
1075 res = read(outfd, buf, CONF_SIZE);
1077 memset(&fr, 0, sizeof(fr));
1078 fr.frametype = AST_FRAME_VOICE;
1079 fr.subclass = AST_FORMAT_SLINEAR;
1083 fr.offset = AST_FRIENDLY_OFFSET;
1084 if (ast_write(chan, &fr) < 0) {
1085 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1089 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1096 /* Take out of conference */
1097 /* Add us to the conference */
1101 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1102 ast_log(LOG_WARNING, "Error setting conference\n");
1106 ast_mutex_lock(&conflock);
1107 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1108 conf_play(conf, LEAVE);
1110 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
1111 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
1112 if ((conf->chan) && (conf->users > 1)) {
1113 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1114 ast_waitstream(conf->chan, "");
1115 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1116 ast_waitstream(conf->chan, "");
1118 ast_filedelete(user->namerecloc, NULL);
1121 ast_mutex_unlock(&conflock);
1125 ast_mutex_lock(&conflock);
1126 if (confflags & CONFFLAG_MONITORTALKER && dsp)
1129 if (user->user_no) { /* Only cleanup users who really joined! */
1130 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
1135 chan->name, chan->uniqueid, conf->confno, user->user_no);
1138 if (confflags & CONFFLAG_MARKEDUSER)
1139 conf->markedusers--;
1142 /* No more users -- close this one out */
1146 prev->next = conf->next;
1155 ast_log(LOG_WARNING, "Conference not found\n");
1156 if (conf->recording == MEETME_RECORD_ACTIVE) {
1157 conf->recording = MEETME_RECORD_TERMINATE;
1158 ast_mutex_unlock(&conflock);
1160 ast_mutex_lock(&conflock);
1161 if (conf->recording == MEETME_RECORD_OFF)
1163 ast_mutex_unlock(&conflock);
1167 ast_hangup(conf->chan);
1172 /* Remove the user struct */
1173 if (user == conf->firstuser) {
1174 if (user->nextuser) {
1175 /* There is another entry */
1176 user->nextuser->prevuser = NULL;
1178 /* We are the only entry */
1179 conf->lastuser = NULL;
1181 /* In either case */
1182 conf->firstuser = user->nextuser;
1183 } else if (user == conf->lastuser){
1185 user->prevuser->nextuser = NULL;
1187 ast_log(LOG_ERROR, "Bad bad bad! We're the last, not the first, but nobody before us??\n");
1188 conf->lastuser = user->prevuser;
1191 user->nextuser->prevuser = user->prevuser;
1193 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
1195 user->prevuser->nextuser = user->nextuser;
1197 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
1200 /* Return the number of seconds the user was in the conf */
1201 snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (time(NULL) - user->jointime));
1202 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1205 ast_mutex_unlock(&conflock);
1209 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
1211 struct ast_config *cfg;
1212 struct ast_variable *var;
1213 struct ast_conference *cnf;
1215 /* Check first in the conference list */
1216 ast_mutex_lock(&conflock);
1219 if (!strcmp(confno, cnf->confno))
1223 ast_mutex_unlock(&conflock);
1227 /* No need to parse meetme.conf */
1228 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1230 if (dynamic_pin[0] == 'q') {
1231 /* Query the user to enter a PIN */
1232 ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
1234 cnf = build_conf(confno, dynamic_pin, make, dynamic);
1236 cnf = build_conf(confno, "", make, dynamic);
1239 /* Check the config */
1240 cfg = ast_config_load("meetme.conf");
1242 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
1245 var = ast_variable_browse(cfg, "rooms");
1247 if (!strcasecmp(var->name, "conf")) {
1248 /* Separate the PIN */
1251 if ((pin = ast_strdupa(var->value))) {
1252 conf = strsep(&pin, "|,");
1253 if (!strcasecmp(conf, confno)) {
1254 /* Bingo it's a valid conference */
1256 cnf = build_conf(confno, pin, make, dynamic);
1258 cnf = build_conf(confno, "", make, dynamic);
1266 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1268 ast_config_destroy(cfg);
1270 } else if (dynamic_pin) {
1271 /* Correct for the user selecting 'D' instead of 'd' to have
1272 someone join into a conference that has already been created
1274 if (dynamic_pin[0] == 'q')
1275 dynamic_pin[0] = '\0';
1280 /*--- count_exec: The MeetmeCount application */
1281 static int count_exec(struct ast_channel *chan, void *data)
1283 struct localuser *u;
1285 struct ast_conference *conf;
1287 char *confnum, *localdata;
1290 if (!data || ast_strlen_zero(data)) {
1291 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1294 localdata = ast_strdupa(data);
1296 confnum = strsep(&localdata,"|");
1297 conf = find_conf(chan, confnum, 0, 0, NULL);
1299 count = conf->users;
1303 if (localdata && !ast_strlen_zero(localdata)){
1304 /* have var so load it and exit */
1305 snprintf(val,sizeof(val), "%i",count);
1306 pbx_builtin_setvar_helper(chan, localdata,val);
1308 if (chan->_state != AST_STATE_UP)
1310 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1312 LOCAL_USER_REMOVE(u);
1316 /*--- conf_exec: The meetme() application */
1317 static int conf_exec(struct ast_channel *chan, void *data)
1320 struct localuser *u;
1321 char confno[AST_MAX_EXTENSION] = "";
1324 struct ast_conference *cnf;
1327 int empty = 0, empty_no_pin = 0;
1328 int always_prompt = 0;
1329 char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
1331 if (!data || ast_strlen_zero(data)) {
1338 if (chan->_state != AST_STATE_UP)
1341 info = ast_strdupa((char *)notdata);
1344 char *tmp = strsep(&info, "|");
1345 strncpy(confno, tmp, sizeof(confno) - 1);
1346 if (ast_strlen_zero(confno)) {
1351 inflags = strsep(&info, "|");
1353 inpin = strsep(&info, "|");
1355 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
1358 if (strchr(inflags, 'a'))
1359 confflags |= CONFFLAG_ADMIN;
1360 if (strchr(inflags, 'T'))
1361 confflags |= CONFFLAG_MONITORTALKER;
1362 if (strchr(inflags, 'i'))
1363 confflags |= CONFFLAG_INTROUSER;
1364 if (strchr(inflags, 'm'))
1365 confflags |= CONFFLAG_MONITOR;
1366 if (strchr(inflags, 'p'))
1367 confflags |= CONFFLAG_POUNDEXIT;
1368 if (strchr(inflags, 's'))
1369 confflags |= CONFFLAG_STARMENU;
1370 if (strchr(inflags, 't'))
1371 confflags |= CONFFLAG_TALKER;
1372 if (strchr(inflags, 'q'))
1373 confflags |= CONFFLAG_QUIET;
1374 if (strchr(inflags, 'M'))
1375 confflags |= CONFFLAG_MOH;
1376 if (strchr(inflags, 'x'))
1377 confflags |= CONFFLAG_MARKEDEXIT;
1378 if (strchr(inflags, 'X'))
1379 confflags |= CONFFLAG_EXIT_CONTEXT;
1380 if (strchr(inflags, 'A'))
1381 confflags |= CONFFLAG_MARKEDUSER;
1382 if (strchr(inflags, 'b'))
1383 confflags |= CONFFLAG_AGI;
1384 if (strchr(inflags, 'w'))
1385 confflags |= CONFFLAG_WAITMARKED;
1386 if (strchr(inflags, 'r'))
1387 confflags |= CONFFLAG_RECORDCONF;
1388 if (strchr(inflags, 'd'))
1390 if (strchr(inflags, 'D')) {
1393 strncpy(the_pin, "q", sizeof(the_pin) - 1);
1396 if (strchr(inflags, 'e'))
1398 if (strchr(inflags, 'E')) {
1402 if (strchr(inflags, 'P'))
1411 struct ast_config *cfg;
1412 struct ast_variable *var;
1415 memset(map, 0, sizeof(map));
1417 ast_mutex_lock(&conflock);
1420 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
1421 /* Disqualify in use conference */
1422 if (confno_int >= 0 && confno_int < 1024)
1427 ast_mutex_unlock(&conflock);
1429 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
1430 if ((empty_no_pin) || (!dynamic)) {
1431 cfg = ast_config_load("meetme.conf");
1433 var = ast_variable_browse(cfg, "rooms");
1435 if (!strcasecmp(var->name, "conf")) {
1436 char *stringp = ast_strdupa(var->value);
1438 char *confno_tmp = strsep(&stringp, "|,");
1440 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
1441 if ((confno_int >= 0) && (confno_int < 1024)) {
1442 if (stringp && empty_no_pin) {
1448 /* For static: run through the list and see if this conference is empty */
1449 ast_mutex_lock(&conflock);
1452 if (!strcmp(confno_tmp, cnf->confno)) {
1453 /* The conference exists, therefore it's not empty */
1459 ast_mutex_unlock(&conflock);
1461 /* At this point, we have a confno_tmp (static conference) that is empty */
1462 if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) {
1463 /* Case 1: empty_no_pin and pin is nonexistant (NULL)
1464 * Case 2: empty_no_pin and pin is blank (but not NULL)
1465 * Case 3: not empty_no_pin
1467 strncpy(confno, confno_tmp, sizeof(confno) - 1);
1469 /* XXX the map is not complete (but we do have a confno) */
1474 ast_log(LOG_ERROR, "Out of memory\n");
1479 ast_config_destroy(cfg);
1482 /* Select first conference number not in use */
1483 if (ast_strlen_zero(confno) && dynamic) {
1484 for (i=0;i<1024;i++) {
1486 snprintf(confno, sizeof(confno), "%d", i);
1493 if (ast_strlen_zero(confno)) {
1494 res = ast_streamfile(chan, "conf-noempty", chan->language);
1496 ast_waitstream(chan, "");
1498 if (sscanf(confno, "%d", &confno_int) == 1) {
1499 res = ast_streamfile(chan, "conf-enteringno", chan->language);
1501 ast_waitstream(chan, "");
1502 res = ast_say_digits(chan, confno_int, "", chan->language);
1505 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
1509 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
1510 /* Prompt user for conference number */
1511 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
1513 /* Don't try to validate when we catch an error */
1519 if (!ast_strlen_zero(confno)) {
1520 /* Check the validity of the conference */
1521 cnf = find_conf(chan, confno, 1, dynamic, the_pin);
1523 res = ast_streamfile(chan, "conf-invalid", chan->language);
1525 ast_waitstream(chan, "");
1530 if (!ast_strlen_zero(cnf->pin)) {
1531 char pin[AST_MAX_EXTENSION]="";
1534 /* Allow the pin to be retried up to 3 times */
1535 for (j=0; j<3; j++) {
1536 if (*the_pin && (always_prompt==0)) {
1537 strncpy(pin, the_pin, sizeof(pin) - 1);
1540 /* Prompt user for pin if pin is required */
1541 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
1544 if (!strcasecmp(pin, cnf->pin)) {
1547 /* Run the conference */
1548 res = conf_run(chan, cnf, confflags);
1552 res = ast_streamfile(chan, "conf-invalidpin", chan->language);
1554 ast_waitstream(chan, AST_DIGIT_ANY);
1569 /* Don't retry pin with a static pin */
1570 if (*the_pin && (always_prompt==0)) {
1575 /* No pin required */
1578 /* Run the conference */
1579 res = conf_run(chan, cnf, confflags);
1583 } while (allowretry);
1584 /* Do the conference */
1585 LOCAL_USER_REMOVE(u);
1589 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
1590 struct ast_conf_user *user = NULL;
1591 char usrno[1024] = "";
1592 if (conf && callerident) {
1593 user = conf->firstuser;
1595 snprintf(usrno, sizeof(usrno), "%i", user->user_no);
1596 if (strcmp(usrno, callerident) == 0)
1598 user = user->nextuser;
1604 /*--- admin_exec: The MeetMeadmin application */
1605 /* MeetMeAdmin(confno, command, caller) */
1606 static int admin_exec(struct ast_channel *chan, void *data) {
1607 char *params, *command = NULL, *caller = NULL, *conf = NULL;
1608 struct ast_conference *cnf;
1609 struct ast_conf_user *user = NULL;
1611 ast_mutex_lock(&conflock);
1612 /* The param has the conference number the user and the command to execute */
1613 if (data && !ast_strlen_zero(data)) {
1614 params = ast_strdupa((char *) data);
1615 conf = strsep(¶ms, "|");
1616 command = strsep(¶ms, "|");
1617 caller = strsep(¶ms, "|");
1620 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
1621 ast_mutex_unlock(&conflock);
1626 if (strcmp(cnf->confno, conf) == 0)
1632 user = find_user(cnf, caller);
1635 switch((int) (*command)) {
1636 case 76: /* L: Lock */
1639 case 108: /* l: Unlock */
1642 case 75: /* K: kick all users*/
1643 user = cnf->firstuser;
1645 user->adminflags |= ADMINFLAG_KICKME;
1646 if (user->nextuser) {
1647 user = user->nextuser;
1653 case 101: /* e: Eject last user*/
1654 user = cnf->lastuser;
1655 if (!(user->userflags & CONFFLAG_ADMIN)) {
1656 user->adminflags |= ADMINFLAG_KICKME;
1659 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
1661 case 77: /* M: Mute */
1663 user->adminflags |= ADMINFLAG_MUTED;
1665 ast_log(LOG_NOTICE, "Specified User not found!\n");
1668 case 78: /* N: Mute all users */
1669 user = cnf->firstuser;
1671 if (user && !(user->userflags & CONFFLAG_ADMIN))
1672 user->adminflags |= ADMINFLAG_MUTED;
1673 if (user->nextuser) {
1674 user = user->nextuser;
1680 case 109: /* m: Unmute */
1681 if (user && (user->adminflags & ADMINFLAG_MUTED)) {
1682 user->adminflags ^= ADMINFLAG_MUTED;
1684 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
1687 case 110: /* n: Unmute all users */
1688 user = cnf->firstuser;
1690 if (user && (user-> adminflags & ADMINFLAG_MUTED)) {
1691 user->adminflags ^= ADMINFLAG_MUTED;
1693 if (user->nextuser) {
1694 user = user->nextuser;
1700 case 107: /* k: Kick user */
1702 user->adminflags |= ADMINFLAG_KICKME;
1704 ast_log(LOG_NOTICE, "Specified User not found!");
1709 ast_log(LOG_NOTICE, "Conference Number not found\n");
1712 ast_mutex_unlock(&conflock);
1716 static void *recordthread(void *args)
1718 struct ast_conference *cnf;
1719 struct ast_frame *f=NULL;
1721 struct ast_filestream *s;
1724 cnf = (struct ast_conference *)args;
1725 if( !cnf || !cnf->chan ) {
1728 ast_stopstream(cnf->chan);
1729 flags = O_CREAT|O_TRUNC|O_WRONLY;
1730 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
1733 cnf->recording = MEETME_RECORD_ACTIVE;
1734 while (ast_waitfor(cnf->chan, -1) > -1) {
1735 f = ast_read(cnf->chan);
1740 if (f->frametype == AST_FRAME_VOICE) {
1741 res = ast_writestream(s, f);
1746 if (cnf->recording == MEETME_RECORD_TERMINATE) {
1747 ast_mutex_lock(&conflock);
1748 ast_mutex_unlock(&conflock);
1752 cnf->recording = MEETME_RECORD_OFF;
1758 int unload_module(void)
1760 STANDARD_HANGUP_LOCALUSERS;
1761 ast_cli_unregister(&cli_show_confs);
1762 ast_cli_unregister(&cli_conf);
1763 ast_unregister_application(app3);
1764 ast_unregister_application(app2);
1765 return ast_unregister_application(app);
1768 int load_module(void)
1770 ast_cli_register(&cli_show_confs);
1771 ast_cli_register(&cli_conf);
1772 ast_register_application(app3, admin_exec, synopsis3, descrip3);
1773 ast_register_application(app2, count_exec, synopsis2, descrip2);
1774 return ast_register_application(app, conf_exec, synopsis, descrip);
1778 char *description(void)
1786 STANDARD_USECOUNT(res);
1792 return ASTERISK_GPL_KEY;